Implementing bound method using descriptor - python

My question is related to bound method object, more specifically I am trying to imitate the same behavior that functions (which are essentially descriptors) present.
Let me first put here my understanding of how class_instance.function() works
class D(object):
def dfunc(self):
...
d=D()
d.dfunc() # will call D.__dict__['dfunc'].__get__(d,D)()
# gives a bound method which can be used directly
a=D.__dict__['dfunc'].__get__(d,D)
a() # works as d.D()
Does Python call D.__dict__['dfunc'] (d), in case when type(dfunc) is function and type(d).__dict__['dfunc'].__get__(d, type(d)) if x is a class instance?
Now coming to how I am trying to create a bound object using closure:
class E(object):
def __get__(self,obj,cls):
def returned(*args):
print(obj.__dict__)
return returned
class F(object):
e = E()
y = 9
f = F()
f.fvar = 9 # putting something in instances __dict__
a = f.e # will call the __get__ function of F and return a closure
a() # works and produces instance specific result -> {'fvar': 9}, just like a bound method
Now the main question,
when I did a = D.__dict__['dfunc'].__get__(d,D) type(a) was bound method, but when I implement it type(a) is function; is there a way to return bound object from user-defined descriptor?
Is there any better way to implement a function like descriptor?
Please correct me if I have gotten something fundamentally incorrect in my head.

If you want to return a method instead, you can create one from the function object together with the arguments passed to __get__:
import types
class E(object):
def __get__(self,obj,cls):
def returned(*args):
print(obj.__dict__)
return types.MethodType(returned, obj, cls)
However, it's not totally clear what this gains you. If just returning a function already works, why do you need it to be a "real" method?

Related

How to override a method in python of an object and call super?

I have an Object of the following class which inherates from the algorithm class.
class AP(Algorithm):
def evaluate(self, u):
return self.stuff *2 +u
The Algorithm class has a method called StoppingCritiria.
At some point in the project the object objAP = AP() gets created. Later on I can then actually access it.
And at that point in time I want to override the method StoppingCriteria by some function which calls the old StoppingCriteria.
I tried simply
def new_stopping(self):
return super().StoppingCriteria() and custom(self.u)
objAP.StoppingCriteria = newStoppingCriteria
But that did not work. What did work were two rather inconviniend solutions:
New AP class (not desirable since I possibly need to do that for lots of classes)
class AP_custom(AP):
def StoppingCriteria(self):
return super().StoppingCriteria() and custom(self)
Override the Method but not using super but rather copy pasting the code into the new function and adding my code to that. Not desirable since I want to changes in the original method to be applyed to my new function as well.
See Override a method at instance level for many possible solutions. None of them will really work with super though, since you're simply not defining the replacement function in a class. You can define it slightly differently though for it to work:
class Foo:
def bar(self):
print('bar')
f = Foo()
def _bar(self):
type(self).bar(self) # or Foo.bar(self)
print('baz')
from typing import MethodType
f.bar = MethodType(_bar, f)
f.bar() # outputs bar baz
Since you're replacing the method at the instance level, you don't really need to access the method of the super class, you just want to access the method of the class, which still exists in its original form.

How to store functions as class variables in python?

I am writing a framework, and I want my base class to use different functions for renaming in the child classes. I figured the best way would be to use a class attribute, like in case of A, but I got TypeErrors when running it like in rename_columns(). However it worked with implementation like B
import pandas as pd
class A:
my_func_mask = str.lower
foo = 'bar'
def rename_columns(self, data):
return data.rename(columns=self.my_func_mask)
class B(A):
def rename_columns(self, data):
return data.rename(columns=self.__class__.my_func_mask)
So I experimented with the above a bit, and I get the following:
a = A()
a.foo # Works fine, gives back 'bar'
a.__class__.my_func_mask # Works as expected `a.__class__.my_func_mask is str.lower` is true
a.my_func_mask # throws TypeError: descriptor 'lower' for 'str' objects doesn't apply to 'A' object
My questions would be why can I use regular typed (int, str, etc.) values as class attributes and access them on the instance as well, while I cannot do that for functions?
What happens during the attribute lookup in these cases? What is the difference in the attribute resolution process?
Actually both foo and my_func_mask is in __class__.__dict__ so I am a bit puzzled. Thanks for the clarifications!
You are storing an unbound built-in method on your class, meaning it is a descriptor object. When you then try to access that on self, descriptor binding applies but the __get__ method called to complete the binding tells you that it can't be bound to your custom class instances, because the method would only work on str instances. That's a strict limitation of most methods of built-in types.
You need to store it in a different manner; putting it inside another container, such as a list or dictionary, would avoid binding. Or you could wrap it in a staticmethod descriptor to have it be bound and return the original. Another option is to not store this as a class attribute, and simply create an instance attribute in __init__.
But in this case, I'd not store str.lower as an attribute value, at all. I'd store None and fall back to str.lower when you still encounter None:
return data.rename(columns=self.my_func_mask or str.lower)
Setting my_func_mask to None is a better indicator that a default is going to be used, clearly distinguishable from explicitly setting str.lower as the mask.
You need to declare staticmethod.
class A:
my_func_mask = staticmethod(str.lower)
foo = 'bar'
>>> A().my_func_mask is str.lower
>>> True
Everything that is placed in the class definition is bound to the class, but you can't bind a built-in to your own class.
Essentially, all code that you place in a class is executed when the class is created. All items in locals() are then bound to your class at the end of the class. That's why this also works to bind a method to your class:
def abc(self):
print('{} from outside the class'.format(self))
class A:
f1 = abc
f2 = lambda self: print('{} from lambda'.format(self))
def f3(self):
print('{} from method'.format(self))
To not have the function bound to your class, you have to place it in the __init__ method of your class:
class A:
def __init__(self):
self.my_func_mask = str.lower

Python class variable changed a function into a method, why?

Why does Python turn a free function into an unbound method upon assignment to a class variable?
def make_func(s):
def func(x):
return '%s-%d' % (s, x)
return func
class Foo(object):
foofunc = make_func('foo')
So this works as expected: (returns "dog-1")
make_func('dog')(1)
But this fails with:
Foo.foofunc(1)
TypeError: unbound method func() must be called with Foo instance as first argument (got int instance instead)
Upon closer inspection, Python turned the "inner" function func inside make_func into a method, but since there's no self, this method will never work. Why does Python do this?
>>> import inspect
>>> inspect.getmembers(Foo, inspect.ismethod)
[('foofunc', <unbound method Foo.func>)]
Python can't tell "how" you assigned a method to a class attribute. There is no difference between this:
class Foo(object):
def meth():
pass
and this
def func():
pass
class Foo(object):
meth = func
In both cases, the result is that a function object is assigned to a class attribute named 'meth'. Python can't tell whether you assigned it by defining the function inside the class, or by "manually" assigning it using meth = func. It can only see the "end result", which is an attribute whose value is a function. Either way, once the function is in the class, it is converted to a method via the normal process that notices functions in class definitions and makes them into methods.
class Foo(object):
foofunc = make_func('foo')
foofunc is a class variable, not a method (for which you need the 'def'). And you initialize it with the result of make_func(...), so it won't change again.
If you want to call Foo.foofunc, you need to assign foofunc = make_func without a parameter.

Python and initialization of class variables - how to use class method to initialize members?

I want to initialize class member with use class method but not know how to call it.
Can you suggest some solution - maybe it is very trivial but I can not find solution?
This code will not work - I do not why?
class X(object):
#staticmethod
def __Y():
return 1
CONSTANT = __Y()
x = X()
print x.CONSTANT
This will work but I need use call method to initialize class members.
class X(object):
CONSTANT = 1
x = X()
print x.CONSTANT
Note, I am not want initialize object variables but class variable.
Drop the #staticmethod decorator and the first approach will work as well. You don't need staticmethod just to call a function inside a class statement.
Since that way the function will not be usable when called from class instances, it is an idiom to also remove it after use. In your example:
class X(object):
def __y():
return 1
CONSTANT = __y()
# ... other uses of __y, if any
del __y
To understand why your approach didn't work, consider what staticmethod does. It wraps a normal function into a descriptor object that, when retrieved from the class, produces the original function unchanged, i.e. without the usual "bound method" semantics. (Retrieving a normal def function from an instance or a class would get you a bound method that automagically inserts self as the first argument.)
However, the descriptor returned by staticmethod is itself not callable, its sole function is to produce the callable object when accessed through the class or instance. Proposals to make the staticmethod descriptor callable were rejected because such use of staticmethod is erroneous in the first place.

Class objects are irregular when assigning functions

Let's say I have a function like this:
def id(x):
return x
I expect to be able to bind it to any object (let's say x),
x.f = id
and be able to do
x.f(1)
=> 1
However, classes are not regular objects because it does not work :
class X(object):
pass
X.f = id
x.f(1)
=> unbound method f() must be called with X instance as first argument (got int instance instead)
So that I have to do,
X.f=staticmethod(id)
X.f(1)
=> 1
However it doesn't work with regular objects,
x.f=staticmethod(f)
x.f(1)
=> 'staticmethod' object is not callable
So is there a way to have assignment work as expected in generic code where
I do not know whether the object assigned to is a regular object of a class object
and when the assigned object may be a function?
import inspect
if inspect.isclass(X):
X.f = staticmethod(f)
else:
X.f = f
The issue isn't the assignment; that works fine. It is that functions on classes take self as their first parameter, and your function doesn't. You can get the actual function back with
X.__dict__['f']
<function f at 0x023A52F0>
or
X.f
<unbound method X.f>
X.f.im_func
<function f at 0x023A52F0>
(You are getting an unbound method instead of the original function, because in Python 2.x that is what happens when you access a method on a class. In Python 3.x that is changed and you just get the original function. I believe it is for backwards compatibility with old-style classes.)
This is a weird thing to want to do: you are working at a very high level of abstraction if you don't even know if the object you are working with is a class or not! For that matter, what if you get passed a built-in type, say a string? You can't set attributes on that! I think you need to work at a slightly lower level.
Define your function that you want to bind like this:
def id_(self, x):
return x
and now you can do something like :
X.f = id_
x = X()
x.f(1)
=> 1
I agree that this is fairly odd behavior. Perhaps there is a reason for this; perhaps it is one of those corner cases that never got fully investigated, or, if it was investigated, was considered so obscure as to not be worth addressing. Sometimes happens.
If you want the behavior that assigning a function as a class attribute always makes it a static method, you can do this with a metaclass.
class func2static(type):
def __setattr__(cls, name, value):
if type(value) is type(lambda: 0):
value = staticmethod(value)
type.__setattr__(cls, name, value)
class X(object):
__metaclass__ = func2static
Now let's show that both the class and instances of the class can have a function assigned to them, which is then callable.
f = lambda x: x
X.f = f
X.f(3) # no exception
x = X()
x.g = f
x.g(3) # also no exception
This doesn't really solve the problem if you are monkey-patching random classes defined in other modules, unfortunately, but you probably shouldn't be doing that anyway. :-)

Categories

Resources