Behavioural difference between decorated function and method in Python - python

I use the following workaround for "Pythonic static variables":
def static_vars(**kwargs):
"""decorator for funciotns that sets static variables"""
def decorate(func):
for k, v in kwargs.items():
setattr(func, k, v)
return func
return decorate
#static_vars(var=1)
def global_foo():
_ = global_foo
print _.var
_.var += 1
global_foo() # >>> 1
global_foo() # >>> 2
It works just as supposed to. But when I move such a decorated function inside a class I get a strange change:
class A(object):
#static_vars(var=1)
def foo(self):
bound = self.foo
unbound = A.foo
print bound.var # OK, print 1 at first call
bound.var += 1 # AttributeError: 'instancemethod' object has no attribute 'var'
def check(self):
bound = self.foo
unbound = A.foo
print 'var' in dir(bound)
print 'var' in dir(unbound)
print bound.var is unbound.var # it doesn't make much sense but anyway
a = A()
a.check() # >>> True
# >>> True
# >>> True
a.foo() # ERROR
I can not see what causes such behaviour. It seems to me that it has something to do with python descriptors protocol, all that bound vs unbound method stuff. Somehow the foo.var attribute is accessible but is not writable.
Any help is appreciated.
P.S. I understand that static function variables are essentially class variables and this decorator is unnecessary in the second case but the question is more for understanding the Python under the hood than to get any working solution.

a.foo doesn't return the actual function you defined; it returns a bound method of it, which wraps up the function and has self assigned toa.
https://docs.python.org/3/howto/descriptor.html#functions-and-methods
That guide is out of date a little, though, since unbound methods just return the function in Python 3.
So, to access the attributes on the function, you need to go through A.foo (or a.foo.__func__)instead of a.foo. And this will only work in Python 3. In Python 2, I think A.foo.__func__ will work.

Related

python class attributes as standard input in a class method

I don't seam to be able to do this but is would make sense that you could.
So mybe I just made a mistake.
class Foobar:
def __init__(self):
self.myatr = 0
def add(self, someinput=self.myatr): # <-- someinput=self.myatr???
return someinput += 1
but you get the error
NameError: name 'self' is not defined
But it would be logicl if the this was the way it worket
f = Foobar()
f.add() # returns 1
f.add(1) # returns 2
Instance methods are functions bound to class attributes, defined when the class is defined, before any instance exists. Similarly, the default value is set once, at definition time, not on-demand when the method is called without an explicit argument.
As such, you need a sentinel (typically None) which signals that no argument was passed.
def add(self, someinput=None):
if someinput is None:
someinput = self.myatr
return someinput + 1
Default arguments are evaluated at function definition. Moreover, the names of the arguments defined earlier (like self in your function) aren't available during function definition. So when you refer to self.myattr, there's no self yet.
For example, consider this function:
>>> def test(thing=print('hello')):
... ...
...
hello
>>>
The expression print('hello') was evaluated right when the function was defined, and it won't be re-evaluated when you call test.
Also, return someinput += 1 is an error too because assignment is not an expression.
Furthermore, integers are always copied, so if you do this:
def test(x):
x += 1
return x
a = 6
test(a)
a will still be equal to six, since the call test(a) copied a.

How to execute nested function from outside

I want to call b() without making it a global function.
def a():
def b():
print(0)
a.b()
>> AttributeError: 'function' object has no attribute 'b'
Is this possible or do i have to make it global?
One way to do this would be to have a return b:
def a():
def b():
print(0)
return b
a()() # prints '0'
b = a()
b() # also prints '0'
In general, you can only access variables that are declared inside a function if they are returned.
This is a pattern you'll see quite frequently in decorator functions, since a decorator typically creates a "wrapper" function locally and then returns the wrapper function as its output.
You certainly can have a nested function. Functions are objects and therefore can have attributes.
def outer():
print ("outer")
def inner():
print("inner")
outer.a = inner # here .a is an attribute of outer
outer()
outer.a()
It's important that you run outer() before running outer.a() because "outer" needs to define "inner".

Is it possible to set a default method for a class in python? [duplicate]

This question already has answers here:
How can I choose a custom string representation for a class itself (not instances of the class)?
(7 answers)
Closed 3 years ago.
Suppose you have a class in python. We'll call it C. And suppose you create an instance of it somewhere in your script, or in interactive mode: c=C()
Is is possible to have a "default" method in the class, such that when you reference the instance, that default method gets called?
class C(object):
def __init__(self,x,y):
self.x=x
self.y=y
def method0(self):
return 0
def method1(self):
return 1
def ...
...
def default(self):
return "Nothing to see here, move along"
And so on.
Now I create an instance of the class in interactive mode, and reference it:
>>> c=C(3,4)
>>> c
<__main__.C object at 0x6ffffe67a50>
>>> print(c)
<__main__.C object at 0x6ffffe67a50>
>>>
Is is possible to have a default method that gets called if you reference the object by itself, as shown below?
>>> c
'Nothing to see here, move along'
>>> print(c)
Nothing to see here, move along
>>>
What you're looking for is the __repr__ method, which returns the string representation of an instance of the class. You can override the method like this:
class C:
def __repr__(self):
return 'Nothing to see here, move along'
so that:
>>> c=C()
>>> c
Nothing to see here, move along
>>> print(c)
Nothing to see here, move along
>>>
Any code that you want to run when an object starts should go in __init__() and if you want to alter the effect of print(instance) you can overwrite the __repr__() of the object. Together it would look like:
class C(object):
def __init__(self, x, y):
self.x = x
self.y = y
print(self.__repr__())
def __repr__(self):
return 'nothing to see here'
c = C(3, 4)
print(c)
Outputting:
nothing to see here
nothing to see here
Where the first is printed when the class is made by calling print(self.__repr__()) and the next print comes from print(c)

Why does this method not return a value?

I have the following code:
class Thing:
def __init__(self):
self.a = 30
self.b = 10
def sumit(self):
return self.a + self.b
giventhing = Thing
print(giventhing.sumit/2)
I get this error:
TypeError: unsupported operand type(s) for /: 'function and 'int'
There are two issues here:
sumit is an instance method, so you need to call it on an instance, not a class (or type).
To execute callables, such as methods, you need to use the propert syntax, which is method(), note the () at the end.
Doing giventhing = Thing won't give you an instance, it will give you a reference to the class/type itself, which is only useful if you want to operate with class members, which is not your use case.
Doing giventhing.sumit / 2, won't divide the result of sumit by 2. In fact, giventhing.sumit will yield a reference to the function itself, not its result. You need to call the function in order to get its return value, i.e. sumit()
Fixed code:
giventhing = Thing() # You need an instance of Thing
print(giventhing.sumit() / 2) # You need to actually call sumit on the instance
sumit is a function: you need to call it with brackets: print(giventhing.sumit()/2)
Functions are type of function you need to first call it
So need:
giventhing = Thing()
print(giventhing.sumit()/2)
So totally need parentheses
Here's an example:
>>> class A:
def __init__(self,a):
self.a=a
def out(self):
return self.a
>>> A
<class '__main__.A'>
>>> a=A(1)
>>> a.out
<bound method A.out of <__main__.A object at 0x0000005F9D177EB8>>
>>> a.out()
1
>>>

Adding a method to a class after its creation in Python

Would it be possible in any way to add a function to an existing instance of a class? (most likely only useful in a current interactive session, when someone wants to add a method without reinstantiating)
Example class:
class A():
pass
Example method to add (the reference to self is important here):
def newMethod(self):
self.value = 1
Output:
>>> a = A()
>>> a.newMethod = newMethod # this does not work unfortunately, not enough args
TypeError: newMethod() takes exactly 1 argument (0 given)
>>> a.value # so this is not existing
Yes, but you need to manually bind it:
a.newMethod = newMethod.__get__(a, A)
Functions are descriptors and are normally bound to instances when looked up as attributes on the instance; Python then calls the .__get__ method for you to produce the bound method.
Demo:
>>> class A():
... pass
...
>>> def newMethod(self):
... self.value = 1
...
>>> a = A()
>>> newMethod
<function newMethod at 0x106484848>
>>> newMethod.__get__(a, A)
<bound method A.newMethod of <__main__.A instance at 0x1082d1560>>
>>> a.newMethod = newMethod.__get__(a, A)
>>> a.newMethod()
>>> a.value
1
Do take into account that adding bound methods on instances does create circular references, which means that these instances can stay around longer waiting for the garbage collector to break the cycle if no longer referenced by anything else.

Categories