Using a method both inside a class and outside - python - python

So I have a function that, so far, I have had as a method inside a class. Turns out now I want to use it without making an instance of the class.
What is the best way of doing this without having to massively change the code?
Example codes follow:
Before:
class A(object):
def method1(self, input):
return input*3 + 7
def method2(self, input):
return self.method1(input) + 4
Basically I want to take method1 out of the class so that i can use it without making an instance of A, but also do not want change self.method1 to method1 everywhere.
My idea:
def method1(input):
return input*3 + 7
class A(object):
def __init__(self):
self.method1 = method1
def method2(self, input):
return self.method1(input) + 4
--
Is this bad practice? How else could one call a method from inside a class? Or alternatively how can a class incorporate methods methods outside it?

Try this:
def method1(input):
return input*3 + 7
class A(object):
def method1(self, input):
return method1(input)
def method2(self, input):
return self.method1(input) + 4
this should work

It won't work because of the self parameter. Instead, define it like this:
class A(object):
def method1(self, input):
return method1(input)

This is called a static method in order to do this, your function can not contain (self)
class A(object):
def method_one(variable):
return variable * 3 + 7
def method_two(self, variable):
return self.method_one(variable) + 4
print(A.method_one(10))
(xenial)vash#localhost:~/python/stack_overflow$ python3.7 method_out.py
37

Turn into a staticmethod the method you don't need or don't want an instance of its class.
The ideia would be like the following:
>>> class A:
#staticmethod
def m(value):
return value*3+7
def sum(self, value):
return self.m(value) + 4
>>> a = A()
>>> a.sum(4)
23
>>> 4+A.m(4)
23
>>>
Notice the difference from a normal method to the static one. On the static one you ommit the self parameter, thus meaning you don't need an instance of its class to use that static method.

Related

Python class static method that requires another static method

Is it ok to have multiple staticmethods that calls from each other.
something like:
class Myclass():
def __init__(self, a):
self.var = self.static1(a)
#staticmethod
def static1(i):
i += 1
return self.static2(i)
#staticmethod
def static2(i):
return i * 3
c = Myclass(1)
I got :
NameError: name 'self' is not defined
use the #classmethod decorator
function will denote it does not change anything in the instance while you can access the class via first parameter cls
class Myclass():
def __init__(self, a):
self.var = self.static1(a)
#classmethod
def static1(cls,i):
i += 1
return cls.static2(i)
#staticmethod
def static2(i):
return i * 3
No need to prepend with 'self.'. Just call MyClass.static2(i)

Parent class method call parent method, not child class method

Is there a way for a child class method to prevent the parent from calling the another child method, but instead call the parent's method?
Here's the code sample I'm referring to:
class A:
def f(self):
return 'Af'
def ff(self, var):
return var + 'ff ' + self.f()
class B(A):
def f(self):
return 'Bf'
def ff(self):
return super().ff('B')
print(B().ff())
The output is Bff Bf
I want it to be: Bff Af
In other words, when B.f() it's called, I would like to redirect the call to A.f() if the calling class is A.
And if the calling class is B then call B.f().
Is this possible?
The problem here is that you haven't designed A for inheritance. For method overriding to work cleanly and without unpredictable side effects, you need to carefully think about and document what methods depend on what other methods.
Also, you have a serious Liskov substitution violation in the fact that B.ff takes 0 arguments when A.ff takes one.
You have three primary options here. One is to use composition instead:
class B:
def __init__(self):
self.a = A()
def f(self):
return 'Bf'
def ff(self):
return self.a.ff('B')
Now B.f has no effect on either class's ff. Given the mismatching A.ff and B.ff signatures, having B inherit from A is probably a bad idea even if you clean up A to support inheritance better.
Option 2 is to decide that f is a hook for customizing ff. In this case, you keep the current A implementation and behavior, and if child classes want to customize ff separately, they need to override that in a way that doesn't depend on A.ff:
class B(A):
def f(self):
return 'Bf'
def ff(self):
return 'Bff ' + super().f()
Option 3 is to decide that overriding f should not affect ff. In that case, A.ff needs to invoke A.f in a way that is not affected by overrides. For example, A.ff could call f as A.f instead of self.f:
class A:
def f(self):
return 'Af'
def ff(self, var):
return var + 'ff ' + A.f(self)
Then your existing B will work as intended. This option is probably the runner-up after option 1.
You can easily do it by not using inheritance (and using composition instead):
class A:
def f(self):
return 'Af'
def ff(self, var):
return var + 'ff ' + self.f()
class B:
def __init__(self):
self.a = A()
def f(self):
return 'Bf'
def ff(self):
return self.a.ff('B')
print(B().ff())
Output:
Bff Af

Calling different parent-class methods with one decorator

So basically my problem seems like this.
class A():
def func(self):
return 3
class B():
def func(self):
return 4
class AA(A):
def func(self):
return super(AA, self).func
class BB(B):
def func(self):
return super(BB, self).func
The func function is doing some work and one of the things it does is getting some attribute(or running method or whatever) from it's parent class.
Since func originally does the same logic at both cases (except that only parent class changes) I'd like to do this with decorators.
Is it possible? if so how to do it? Do I have somehow to pass parent-class as a argument?
I'll be very grateful for answers it's been bothering me for a while now.
There is no need to use super to access data attributes of a parent class.
Neither does a class need a parent in order for access to data attributes to work.
You can use a mixin to do the job:
# A and B stay the same - they still have a c attribute
class A():
c = 3
class B():
c = 4 # I've changed B to make it clear below
#Instead have a mixin which defines func()
class Mixin:
def func(self):
# func has its behaviour here
return self.c
class AA(Mixin, A):
pass
class BB(Mixin, B):
pass
a = AA()
b = BB()
print(a.func())
print(b.func())
Output:
3
4
You could do it with a single class decorator by defining a generic method inside of it that does what you want, and then adding it to the class being decorated. Here's what I mean:
def my_decorator(cls):
def call_super_func(self):
return super(type(self), self).func()
setattr(cls, 'call_super_func', call_super_func)
return cls
class A():
def func(self):
print('in A.func')
return 3
class B():
def func(self):
print('in B.func')
return 4
#my_decorator
class AA(A):
def func(self):
print('in AA.func')
return self.call_super_func()
#my_decorator
class BB(B):
def func(self):
print('in BB.func')
return self.call_super_func()
aa = AA()
aa.func()
bb = BB()
bb.func()
Output:
in AA.func
in A.func
in BB.func
in B.func
Of course you could eliminate the need to do this by just defining baseclass for A and B that has a call_super_func() method in it that they would then both inherit.

Classmethod: Using a function within a function

I have a situation where I'm using #classmethod to create a constructor for a class. Within this constructor, a function gets called, which then in turn calls another function. But either this doesn't work or (more probably) I'm doing something to make it not work. Here's an example in miniature:
class testclass:
def __init__(self, x):
self.x = x
#classmethod
def constructor(cls, x):
adj_x = cls.outer_adjust(cls, x)
return testclass(adj_x)
def outer_adjust(self, x):
return self.inner_adjust(x)
def inner_adjust(self, x):
return x + 1
test_instance = testclass.constructor(4)
This produces an error message:
inner_adjust() missing 1 required positional argument: 'x'
I can make it work by explicitly passing self to inner_adjust, eg
def outer_adjust(self, x):
return self.inner_adjust(self, x)
But this then means that the outer_adjust method can't be used outside of the constructor, which is not what I want.
Any assistance gratefully received.
Here's a more detailed example, with two constructors shown. I'm trying to follow the approach to constructors described in
What is a clean, pythonic way to have multiple constructors in Python?
Which is essentially that the constructors do some processing to figure out what variables they should pass to init when instantiating the class.
Both constructors give the same error:
if_char_is_z_make_it_a() missing 1 required positional argument: 'char_input'
As before, I need to be able to use the if_char_is_make_it_a function outside of the constructor (ie, when using the class normally).
class testclass:
def __init__(self, char):
self.char = char
#classmethod
def constructor_from_int(cls, int_input):
as_char = chr(int_input)
char = cls.process_char(cls, as_char)
return testclass(char)
#classmethod
def constructor_from_char(cls, char_input):
char = cls.process_char(cls, char_input)
return testclass(char)
def process_char(self, char_input):
processed_char = '(' + char_input + ')'
output_char = self.if_char_is_z_make_it_a(processed_char)
return output_char
def if_char_is_z_make_it_a(self, char_input):
if char_input == '(z)':
return '(a)'
return char_input
test_instance = testclass.constructor_from_char('a')
When you call cls.outer_adjust from constructor you are calling the unbound outer_adjust method.
Thus, you pass the class itself as self and not an instance to a method that expects to receive an instance as argument.
Although, there is no real reason to have a constructor method. This is exactly what __init__ is for.
class testclass:
def __init__(self, x):
self.x = self.outer_adjust(x)
def outer_adjust(self, x):
return self.inner_adjust(x)
def inner_adjust(self, x):
return x + 1
test_instance = testclass(4)
If you absolutely need the transformation on x to be done before the instantiation, then use __new__ instead. Although, this is generally not necessary.
Multiple constructors
If for some reason you still need to have a constructor method, by example if you want multiple constructors. Then keep in mind that outer_adjust and inner_adjust are instance methods, this means they must be called after you have created an instance.
class testclass:
def __init__(self, x):
self.x = x
#classmethod
def constructor1(cls, x):
instance = cls(x)
instance.outer_adjust()
return instance
#classmethod
def constructor2(cls, x):
instance = cls(x)
instance.inner_adjust()
return instance
def outer_adjust(self):
print('Do something else')
return self.inner_adjust()
def inner_adjust(self):
self.x += 1
As a sidenote, notice how I did not need to call testclass, but simply called cls in the constructor methods. Since this is a class method, we do not need to explicitly name the class. This is better, especially if you are to use inheritance.
Basically what you are doing here shall be done via the __new__ which serve as constructor.
class testclass:
def __init__(self, x):
self.x = x
def __new__(cls, *args, **kwargs):
instance = super(testclass, cls).__new__(cls, *args, **kwargs)
instance.outer_adjust(args[0])
return instance
def outer_adjust(self, x):
return self.inner_adjust(x)
def inner_adjust(self, x):
self.x = x + 1
test_instance = testclass(4)
You are abusing self. The point of the class method is to use the cls argument as constructor, instead of explicitly naming the class by testclass(adj_x). Also, during the cls.outer_adjust(cls, x) call, you are passing the class instead of the instance, which happens to work because you are not using any instance attributes.
As to your questions, there's no way to avoid the x argument. inner_adjust increases some value by 1, so you must give it something to increase. The idea would be to have
def constructor(cls, x):
return cls(x)
def inner_adjust(self):
return self.x += 1
and then do something like
object= testclass.constructor(12)
object.inner_adjust()

How do I pass a whole class as a parameter to another class`s method

Is it possible to pass a whole class (not an instance) as a parameter to another class method in Python? If I have several instances of the class first and need to pass any of them not specifying which one to method of class Second, can I do something like this:
class First():
def __init__(self, a, b):
pass
class Second():
def __init__(self, c, d):
pass
def method(self, First):
#and how do I call here the whole class First
#without calling a particular instance here?
Straightforward.
def method(self, First):
First() #instantiation
First.classmethod()
First.staticmethod()
In python classes are objects itself, so you are able to call your method like this
second_instance.method(Any_Class_You_Want)
First, you don't need to specify types in Python. So, if you want method to take a First instance, just do this:
class Second():
def __init__(self, c, d):
pass
def method(self, first):
pass
my_first = First(0, 1)
my_second = Second(2, 3)
my_second.method(my_first)
I believe that answers your real question, which is:
If I have several instances of the class first and need to pass any of them not specifying which one to method of class Second…
If you want to ensure that the parameter actually is a First, you can always add an assert isinstance(first, First) or if not isinstance(first, First): raise TypeError or whatever, but usually you don't want to do that in Python. The whole point of "duck typing" is that you write a function that takes "anything that acts like a First instance" rather than a function that takes "a First instance".
You then say:
Now I need to mutate variables from the First class inside the method of a second class:
So… just do it. Your example doesn't have any attributes in the First class, so let's add some:
class First():
def __init__(self, a, b):
self.total = a + b
And now, let's use them in Second.method:
class Second():
def __init__(self, c, d):
self.total = c + d
def method(self, first):
first.total += self.total
So:
>>> my_first = First(0, 1)
>>> my_first.total
1
>>> my_second = Second(2, 3)
>>> my_second.total
5
>>> my_first.total += 2
>>> my_first.total
3
>>> my_second.method(my_first)
>>> my_first.total
8
Or, if you meant that you wanted to mutate the class attributes in class First… you don't even need a First instance for that:
First.my_class_attribute = 1
If you really do need to pass a class itself… well, a class is a regular value like anything else:
class Second():
def __init__(self, c, d):
pass
def method(self, cls):
pass
my_second = Second(1, 2)
my_second.method(First)
And you can access the class attributes of cls from within method, just as easily as you can access instance attributes when an instance is passed.
You can do:
class Model1:
def get():
return '1'
class Model2:
def get(Model1):
print('test: '+ str(Model1.get()))
if __name__ == '__main__':
Model2.get(Model1)
the output is;: test: 1

Categories