Function callable as class method or instance method [duplicate] - python

This question already has an answer here:
can a function be static and non-static in python 2
(1 answer)
Closed 4 years ago.
I have a Python class that I created previously, it looks something like this:
class Foo:
#classmethod
def bar(cls, x):
print(x + 3)
Foo.bar(7) # prints '10'
Now I would like to retrofit this interface with some state, so that the caller can create a Foo object, give it some properties, and then call its bar() method, which has access to self and its properties:
class Foo:
def __init__(self, y=3):
self.y = y
def bar(self, x):
print(x + self.y)
Foo().bar(7) # prints '10'
Foo(20).bar(7) # prints '27'
Unfortunately, this breaks the previous interface - Foo.bar(7) will now give TypeError: bar() missing 1 required positional argument: 'x', because Foo.bar(...) is a simple function reference.
I can add a #classmethod decorator to get part way there:
class Foo:
def __init__(self, y=3):
self.y = y
#classmethod
def bar(cls, x):
self = cls()
print(x + self.y)
Foo.bar(7) # prints '10'
Foo().bar(7) # prints '10'
Foo(20).bar(7) # prints '10', but I want '27'
Is it possible to create a #flexmethod decorator that converts the other direction? Specifically - if called as an instance method like Foo(20).bar(...), then do nothing; if called as a class method like Foo.bar(...) then create a new Foo object (using no-arg constructor) and pass that as the self argument.
I was going to try this myself by looking at the source for #classmethod, but it looks like it's implemented at the C level.

The Descriptor HOWTO has entire section on how #classmethod and #staticmethod work, and how to implement variations on them, including this pure-Python equivalent to classmethod:
class ClassMethod(object):
"Emulate PyClassMethod_Type() in Objects/funcobject.c"
def __init__(self, f):
self.f = f
def __get__(self, obj, klass=None):
if klass is None:
klass = type(obj)
def newfunc(*args):
return self.f(klass, *args)
return newfunc
If it isn't obvious to you how that works, you probably need to read the whole HOWTO. I've got a blog post that tries to provide an introduction to the method-related stuff first, which may help get over the abstraction hump of the first part of the HOWTO.

Related

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 modify class method without touching source code in python?

How do I modify class method without touching source code?
There's a class from a library which I don't want to change the source code of.
class SomeLibraryClass(object):
def foo(self, a_var):
print self, a_var, "hello world"
Now, I want to define my own foo method, and substitute for the original SomeLibraryClass.foo .
def foo(obj, a_var):
print obj, a_var, "good bye"
SomeLibraryClass.foo = foo //
What should I do with the self variable?
How can I do this?
The same thing as you would do with self if you were in a method. self will be passed as the first parameter to your function and will be a reference to the current object. So if you want to define a function that will be used as a replacement for a method, just add self as the first parameter.
class SomeLibraryClass(object):
def __init__(self, x):
self.x = x
def foo(self, y):
print('Hello', self.x, y)
def my_foo(self, y):
print ('Good buy', self.x, y)
SomeLibraryClass.foo = my_foo
And how to use it:
>>> s = SomeLibraryClass(33)
>>> s.foo(5)
Good buy 33 5

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

How to detect method overloading in subclasses in python?

I have a class that is a super-class to many other classes. I would like to know (in the __init__() of my super-class) if the subclass has overridden a specific method.
I tried to accomplish this with a class method, but the results were wrong:
class Super:
def __init__(self):
if self.method == Super.method:
print 'same'
else:
print 'different'
#classmethod
def method(cls):
pass
class Sub1(Super):
def method(self):
print 'hi'
class Sub2(Super):
pass
Super() # should be same
Sub1() # should be different
Sub2() # should be same
>>> same
>>> different
>>> different
Is there any way for a super-class to know if a sub-class has overridden a method?
It seems simplest and sufficient to do this by comparing the common subset of the dictionaries of an instance and the base class itself, e.g.:
def detect_overridden(cls, obj):
common = cls.__dict__.keys() & obj.__class__.__dict__.keys()
diff = [m for m in common if cls.__dict__[m] != obj.__class__.__dict__[m]]
print(diff)
def f1(self):
pass
class Foo:
def __init__(self):
detect_overridden(Foo, self)
def method1(self):
print("Hello foo")
method2=f1
class Bar(Foo):
def method1(self):
print("Hello bar")
method2=f1 # This is pointless but not an override
# def method2(self):
# pass
b=Bar()
f=Foo()
Runs and gives:
['method1']
[]
If you want to check for an overridden instance method in Python 3, you can do this using the type of self:
class Base:
def __init__(self):
if type(self).method == Base.method:
print('same')
else:
print('different')
def method(self):
print('Hello from Base')
class Sub1(Base):
def method(self):
print('Hello from Sub1')
class Sub2(Base):
pass
Now Base() and Sub2() should both print "same" while Sub1() prints "different". The classmethod decorator causes the first parameter to be bound to the type of self, and since the type of a subclass is by definition different to its base class, the two class methods will compare as not equal. By making the method an instance method and using the type of self, you're comparing a plain function against another plain function, and assuming functions (or unbound methods in this case if you're using Python 2) compare equal to themselves (which they do in the C Python implementation), the desired behavior will be produced.
You can use your own decorator. But this is a trick and will only work on classes where you control the implementation.
def override(method):
method.is_overridden = True
return method
class Super:
def __init__(self):
if hasattr(self.method, 'is_overridden'):
print 'different'
else:
print 'same'
#classmethod
def method(cls):
pass
class Sub1(Super):
#override
def method(self):
print 'hi'
class Sub2(Super):
pass
Super() # should be same
Sub1() # should be different
Sub2() # should be same
>>> same
>>> different
>>> same
In reply to answer https://stackoverflow.com/a/9437273/1258307, since I don't have enough credits yet to comment on it, it will not work under python 3 unless you replace im_func with __func__ and will also not work in python 3.4(and most likely onward) since functions no longer have the __func__ attribute, only bound methods.
EDIT: Here's the solution to the original question(which worked on 2.7 and 3.4, and I assume all other version in between):
class Super:
def __init__(self):
if self.method.__code__ is Super.method.__code__:
print('same')
else:
print('different')
#classmethod
def method(cls):
pass
class Sub1(Super):
def method(self):
print('hi')
class Sub2(Super):
pass
Super() # should be same
Sub1() # should be different
Sub2() # should be same
And here's the output:
same
different
same
You can compare whatever is in the class's __dict__ with the function inside the method
you can retrieve from the object -
the "detect_overriden" functionbellow does that - the trick is to pass
the "parent class" for its name, just as one does in a call to "super" -
else it is not easy to retrieve attributes from the parentclass itself
instead of those of the subclass:
# -*- coding: utf-8 -*-
from types import FunctionType
def detect_overriden(cls, obj):
res = []
for key, value in cls.__dict__.items():
if isinstance(value, classmethod):
value = getattr(cls, key).im_func
if isinstance(value, (FunctionType, classmethod)):
meth = getattr(obj, key)
if not meth.im_func is value:
res.append(key)
return res
# Test and example
class A(object):
def __init__(self):
print detect_overriden(A, self)
def a(self): pass
#classmethod
def b(self): pass
def c(self): pass
class B(A):
def a(self): pass
##classmethod
def b(self): pass
edit changed code to work fine with classmethods as well:
if it detects a classmethod on the parent class, extracts the underlying function before proceeding.
--
Another way of doing this, without having to hard code the class name, would be to follow the instance's class ( self.__class__) method resolution order (given by the __mro__ attribute) and search for duplicates of the methods and attributes defined in each class along the inheritance chain.
I'm using the following method to determine if a given bound method is overridden or originates from the parent class
class A():
def bla(self):
print("Original")
class B(A):
def bla(self):
print("Overridden")
class C(A):
pass
def isOverriddenFunc(func):
obj = func.__self__
prntM = getattr(super(type(obj), obj), func.__name__)
return func.__func__ != prntM.__func__
b = B()
c = C()
b.bla()
c.bla()
print(isOverriddenFunc(b.bla))
print(isOverriddenFunc(c.bla))
Result:
Overridden
Original
True
False
Of course, for this to work, the method must be defined in the base class.
You can also check if something is overridden from its parents, without knowing any of the classes involved using super:
class A:
def fuzz(self):
pass
class B(A):
def fuzz(self):
super().fuzz()
class C(A):
pass
>>> b = B(); c = C()
>>> b.__class__.fuzz is super(b.__class__, b).fuzz.__func__
False
>>> c.__class__.fuzz is super(c.__class__, c).fuzz.__func__
True
See this question for some more nuggets of information.
A general function:
def overrides(instance, function_name):
return getattr(instance.__class__, function_name) is not getattr(super(instance.__class__, instance), function_name).__func__
>>> overrides(b, "fuzz")
True
>>> overrides(c, "fuzz")
False
You can check to see if the function has been overridden by seeing if the function handle points to the Super class function or not. The function handler in the subclass object points either to the Super class function or to an overridden function in the Subclass. For example:
class Test:
def myfunc1(self):
pass
def myfunc2(self):
pass
class TestSub(Test):
def myfunc1(self):
print('Hello World')
>>> test = TestSub()
>>> test.myfunc1.__func__ is Test.myfunc1
False
>>> test.myfunc2.__func__ is Test.myfunc2
True
If the function handle does not point to the function in the Super class, then it has been overridden.
Not sure if this is what you're looking for but it helped me when I was looking for a similar solution.
class A:
def fuzz(self):
pass
class B(A):
def fuzz(self):
super().fuzz()
assert 'super' in B.__dict__['fuzz'].__code__.co_names
The top-trending answer and several others use some form of Sub.method == Base.method. However, this comparison can return a false negative if Sub and Base do not share the same import syntax. For example, see discussion here explaining a scenario where issubclass(Sub, Base) -> False.
This subtlety is not apparent when running many of the minimal examples here, but can show up in a more complex code base. The more reliable approach is to compare the method defined in the Sub.__bases__ entry corresponding to Base because __bases__ is guaranteed to use the same import path as Sub
import inspect
def method_overridden(cls, base, method):
"""Determine if class overriddes the implementation of specific base class method
:param type cls: Subclass inheriting (and potentially overriding) the method
:param type base: Base class where the method is inherited from
:param str method: Name of the inherited method
:return bool: Whether ``cls.method != base.method`` regardless of import
syntax used to create the two classes
:raises NameError: If ``base`` is not in the MRO of ``cls``
:raises AttributeError: If ``base.method`` is undefined
"""
# Figure out which base class from the MRO to compare against
base_cls = None
for parent in inspect.getmro(cls):
if parent.__name__ == base.__name__:
base_cls = parent
break
if base_cls is None:
raise NameError(f'{base.__name__} is not in the MRO for {cls}')
# Compare the method implementations
return getattr(cls, method) != getattr(base_cls, method)

Preventing a class's function attributes from being passed self as the first arg

Okay, so I've got a class where one of the attributes is a callback function. Problem is, whenever I call it from within the class (e.g. as self.function_attr(), it gets passed self as the first argument. Here's an idea of what I'm working with:
def callback(a, b):
# do something with a, b
class A:
def __init__(self, callback):
self.callback = callback
self.callback(1, 2) # Raises a TypeError: takes exactly 2 arguments (3 given)
I'm not willing to write each callback function to take self as a first argument. I wrote a decorator that works around the issue:
def callback_decorator(func):
def newfunc(self, *args, **kw):
return func(*args, **kw)
return newfunc
but I'm wondering if there's anything better.
Basically, my question is, how can I call instance attributes of my class which are functions without them being passed self as the first argument?
You just need to make it a staticmethod when you bind it to the class.
def callback(a, b):
# do something with a, b
class A:
def __init__(self, callback):
# now it won't get passed self
self.callback = staticmethod(callback)
self.callback(1, 2)
or
class A:
def __init__(self, callback):
self.callback(1, 2)
# now it won't get passed self
callback = staticmethod(callback)
As far as I know, a wrapper (like your decorator) is the simplest way to go. Since you already have an object in which to store the function, I wouldn't bother with a decorator. (Note I've inherited from object, which is something you should probably be doing unless you specifically want old-style class behaviour.)
class A(object):
def __init__(self, callback):
self._callback = callback
self.callback(1,2)
def callback(self, *args, **kwargs):
return self._callback(*args, **kwargs)
This behaves as you'd expect:
>>> def f(x, y):
... print "X: %s, Y: %s" % (x,y)
...
>>> mya = A(f)
X: 1, Y: 2

Categories