python - Overloading operators on the fly - python

I would like to know, if there is a way to overload operators in Python in runtime. For instance:
class A:
pass
a = A()
a.__str__ = lambda self: "noice"
print(str(a))
The desired output is "noice", but the given code uses object's implementation of the str function instead, yielding something along the lines: <__main__.A object at 0x000001CAB2051490>.
Why doesn't the code use my overriden implementation of the function overload?
Python version used is 3.9.2.

When you call str(a), it resolves to the equivalent of a.__class__.__str__(a), not a.__str__().
>>> A.__str__ = lambda self: "noice"
>>> str(a)
'noice'

You have to assign that function to the class, not an instance of the class.
>>> class A:
... pass
...
>>> a = A()
>>> a.__str__ = lambda x: 'hi'
>>> print(a)
<__main__.A object at 0x000000A4D16C1D30>
>>> A.__str__ = lambda x: 'hi'
>>> print(a)
hi

Related

How to get the id of the underlying function bound to an object by a method-wrapper?

Consider the following case:
>>> "a".capitalize.__call__
<method-wrapper '__call__' of builtin_function_or_method object at 0x1022e70d8>
>>> "a".capitalize.__call__.__call__
<method-wrapper '__call__' of method-wrapper object at 0x1022e2b70>
>>> id("a".capitalize.__call__)
4331547448
>>> id("a".capitalize.__call__.__call__)
4331547504
>>> id("a".capitalize.__call__) == id("a".capitalize.__call__.__call__)
False
How can I establish at runtime that the __call__ refers to the same base symbol in both cases ?
Edit:
It is possible that expecting something like this to exist for __call__ in the first place is unreasonable because __call__ might not have a base symbol that is not bound to any object - in which case what is the best way to detect that other than keeping a list of special names (how can such a list be built authoritatively ?) ?
Won't work for builtin_function_or_method as they have no __func__.
If you're only working with your classes you can compare __func__:
>>> class Foo:
... def bar(self):
... pass
...
>>>
>>> a = Foo()
>>> b = Foo()
>>> a.bar.__func__
<function Foo.bar at 0x7f6f103e5ae8>
>>> a.bar.__func__ is b.bar.__func__
True
But I'd say it's better to do it the other way around: instead of trying to get the function from the instance, get up to the type first:
type(a).bar is type(a).bar
Which should work even for bltns:
>>> type("").capitalize is type("a").capitalize
True
And for hierarchies:
>>> class A:
... def foo(self):...
...
>>> class B(A):...
...
>>> a = A()
>>> b = B()
>>> type(a).foo is type(b).foo
True

how to understand this example of __getattr__ as metaclass method

Here is a snippet of example from Mark Lutz's book "Learning Python". I found it difficult to understand as to how name accesses are translated into getattr() calls in the metaclass:
>>> class A(type):
def __getattr__(cls, name):
return getattr(cls.data, name)
>>> class B(metaclass=A):
data = 'spam'
>>> B.upper()
'SPAM'
>>> B.upper
<built-in method upper of str object at 0x029E7420>
>>> B.__getattr__
<bound method A.__getattr__ of <class '__main__.B'>>
>>> B.data = [1, 2, 3]
>>> B.append(4)
>>> B.data
[1, 2, 3, 4]
>>> B.__getitem__(0)
1
>>> B[0]
TypeError: 'A' object does not support indexing
I have the following questions:
how does B.upper() yield 'SPAM'? Is it because B.upper() => A.__getattr__(B, upper()) => getattr(B.data, upper())? but a call like getattr('spam', upper()) gives error "NameError: name 'upper' is not defined"
what path does B.upper go to yiled <built-in method upper of str object at 0x029E7420>. does it go through getattr too, what is the true value of the arguments?
Does B.append(4) go through A.__getattr__(cls, name)? if it does, what is the true values of the arguments in getattr(cls.data, name) in this case?
how does B.__getitem__(0) yield 1? what is the true values of the arguments in getattr(cls.data, name) in this case?
B.upper() first looks up B.upper and then calls it with no arguments. B.upper is looked up by trying several options in a certain order, eventually trying type(B).__getattr__(B, 'upper'), which in this case is A.__getattr__(B, 'upper'), which returns 'spam'.upper.
As mentioned above, B.upper goes through several options, in this case reaching type(B).__getattr__(B, 'upper') which is A.__getattr__(B, 'upper').
Yes, in this case, B.append will reach A.__getattr__(B, 'append') which will return B.data.append.
B.__getitem__(0) will in this case look up B.__getitem__ and find it via A.__getattr__(B, '__getitem__') which will return B.data.__getitem__.
Also, note the final example, B[0], doesn't work because the B class doesn't directly define a __getitem__ method. This is because "special" methods, such as __getitem__, are looked up differently when used via their special syntax, such as B[0] in this case.
First you don't need to add the usual confusion of a meta class to get this behaviour, you can just as easily use a regular class and an instance to use as an example:
class A():
def __getattr__(cls, name):
return getattr(cls.data, name)
B = A()
B.data = "spam"
>>> B.data
'spam'
>>> B.upper
<built-in method upper of str object at 0x1057da730>
>>> B.upper()
'SPAM'
>>> B.__getitem__
<method-wrapper '__getitem__' of str object at 0x1057da730>
>>> B.__getitem__(0)
's'
>>> B[0]
Traceback (most recent call last):
File "<pyshell#135>", line 1, in <module>
B[0]
TypeError: 'A' object does not support indexing
Next keep in mind that B[0] does not look up B.__getitem__ using your special method but rather tries to access it directly on type(B) which does not have a __getitem__ so the indexing fails.
how does B.upper() yield 'SPAM'? Is it because B.upper() =>
A.getattr(B, upper()) => getattr(B.data, upper())? but a call
like getattr('spam', upper()) gives error "NameError: name 'upper'
is not defined"
getattr('spam', upper()) does not make any sense, the name of the attribute is always a string, so using B.upper (no calling yet) would be equivelent to getattr(B, "upper") then you call the method.
what path does B.upper go to yiled . does it go through getattr too, what is the
true value of the arguments?
Is there any reason you are not just adding a print statement to check?
class A():
def __getattr__(cls, name):
print("calling __getattr__ for this name: %r"%name)
return getattr(cls.data, name)
>>> B = A()
>>> B.data = "spam"
>>> B.upper
calling __getattr__ for this name: 'upper'
<built-in method upper of str object at 0x1058037a0>
both 3 and 4 are answered by adding this print statement:
>>> B.data = [1,2,3,4]
>>> B.append(5)
calling __getattr__ for this name: 'append'
>>> B.__getitem__(0)
calling __getattr__ for this name: '__getitem__'
1

Where does the __closure__ attribute in python bound method come from?

# python3
def foo(a):
class A:
def say(self):
print(a)
return A
A = foo(1)
'__closure__' in dir(A.say) # True
a = A()
a.say.__closure__ # it returns the closure tuple
'__closure__' in dir(a.say) # False
'__closure__' in dir(a.say.__class__) # False
'__closure__' in dir(a.say.__class__.__class__) # False
In Python3, A.say is a function, and I know it has__closure__ attribute.
__closure__ not in dir(a.say) or its super class, but a.say.__closure__ returns the closure tuple. It makes me confuse.
Thanks.
I don't know in Python the internal implementation of objects with type instancemethod but I think it is how __getattr__ works in instance method objects.
My guess is when you say a.say.__closure__ it first looks up for __closure__ in dir(a.say) and then fallbacks on dir(a.say.im_func).
>>> a = foo(1)()
>>> print type(a.say)
>>> instancemethod
>>> a.say.im_func.__closure__
>>> (<cell at 0x10a00f980: int object at 0x7fef29e098b8>,)
>>> '__closure__' in dir(a.say.im_func)
>>> True

Determining the method called within a lambda function

I have a lambda function which is passed to a object and stored as a variable:
f = lambda x: x.method_foo()
I want to determine the name of method called on the variable x as a string. So I want
method_foo
saved as a string.
Any help appreciated.
You could access the lambda's code object with func_code, and access the code's local names with co_names.
>>> f = lambda x: x.method_foo
>>> f.func_code.co_names
('method_foo',)
>>> f.func_code.co_names[0]
'method_foo'
This is a bit "crazy", but you can use pass a Mock to f and get the method that was added to the mock after calling the function:
>>> from mock import Mock
>>> f = lambda x: x.method_foo
>>> m = Mock()
>>> old_methods = dir(m)
>>> f(m)
<Mock name='mock.method_foo' id='4517582608'>
>>> new_methods = dir(m)
>>> next(method for method in new_methods if method not in old_methods)
'method_foo'

python - how can I dynamically create a function with a name based on a string read in from a file?

Let's say I have a file containing the string "unpredictable_words". I would like to read in this string and then define a function as follows:
def test_unpredictable_words(self):
do_important_stuff()
I would then like to inject this function into a class definition so that this function can be called on any instances of this class.
How can I accomplish this?
I looked a bit at this answer - https://stackoverflow.com/a/8160676/1701170 - but I don't think it does what I want, exactly, or at least I can't understand what is going on.
Python 2.7.3 (default, Sep 26 2012, 21:51:14)
>>> def injected(self):
... print 'injected'
...
>>> class A(object):
... pass
...
>>> A.injected = injected
>>> a = A()
>>> a.injected()
injected
>>> def func2(self):
... print 'func2'
...
>>> setattr(A, 'injected2', func2)
>>> a.injected2()
func2
>>>
You don't need to define a function under one true name. Functions are first-class entitiens, you can pass them around and assign to variables. On top level, you use globals(), withing another function, locals() to bind a name:
>>> def foo(x):
... return x + 1
...
>>> name = 'unpredictable_words'
>>>
>>> globals()['test_' + name] = foo
>>>
>>> test_unpredictable_words(1)
2
>>>
>>> def some_function():
... locals()['test_' + name] = foo
... return test_unpredictable_words(1)
...
>>> some_function()
2
>>>
Sometimes you still want that the function knows its name, in order to appear nicely in a stacktrace. Now test_unpredictable_words appear as foo in error messages. This is not easy to fix, since that name is stored in foo.func_code.co_name and can't be changed.

Categories