With the following script:
import time
class Foo(object):
func = time.gmtime
def go(self):
return self.func(0.0)
print time.strftime('%Y', Foo().go())
I get the following output:
1970
However, if I make a slight modification and wrap time.gmtime
:
import time
def tmp(stamp):
return time.gmtime(stamp)
class Foo(object):
func = tmp
def go(self):
return self.func(0.0)
print time.strftime('%Y', Foo().go())
then I get the following error:
Traceback (most recent call last):
File "./test.py", line 13, in <module>
print time.strftime('%Y', Foo().go())
File "./test.py", line 11, in go
return self.func(0.0)
TypeError: tmp() takes exactly 1 argument (2 given)
Obviously it's trying to call Foo.func
as if it was an instance method and passing self
as the first argument.
Two questions:
time.gmtime
and tmp
are functions that take a single argument, so why do the two scripts behave differently?Differences Between Class and Instance Attributes The difference is that class attributes are shared by all instances. When you change the value of a class attribute, it will affect all instances that share the same exact value. The attribute of an instance on the other hand is unique to that instance.
A variable stored in an instance or class is called an attribute. A function stored in an instance or class is called a method.
According to this web-page, class attributes are variables owned by the class itself. The web page says tagDataMap is a class attribute. But according to Tutorialspoint.com, "class variable is a variable that is shared by all instances of a class.
Advantages of class attributes: All instances of the class inherit them from the class. They store data that is relevant to all the instances. For example, we could have a counter class attribute that increments every time we create a new instance and decrements every time we delete an instance.
- Both
time.gmtime
andtmp
are functions that take a single argument, so why do the two scripts behave differently?
Let us understand what Python does when you do
self.func
from the documentation,
When an instance attribute is referenced that isn’t a data attribute, its class is searched. If the name denotes a valid class attribute that is a function object, a method object is created by packing (pointers to) the instance object and the function object just found together in an abstract object: this is the method object. When the method object is called with an argument list, a new argument list is constructed from the instance object and the argument list, and the function object is called with this new argument list.
So, if func
is a valid function object in self
, then a bound method object will be created, which expects the first argument to be the object on which this function is invoked on.
Let us compare now,
print type(time.gmtime)
class Foo(object):
func = time.gmtime
def go(self):
print type(self.func)
return self.func(0.0)
When go
is invoked with Foo().go()
, it will print
<type 'builtin_function_or_method'>
<type 'builtin_function_or_method'>
That's right. Since time.gmtime
is a built-in function (which is different from the function object), there is no bound method object created here. Lets try your second example now,
def tmp(stamp):
return time.gmtime(stamp)
print type(tmp), tmp
class Foo(object):
func = tmp
def go(self):
print type(self.func), self.func
return self.func(0.0)
would print
<type 'function'> <function tmp at 0x7f34d9983578>
<type 'instancemethod'> <bound method Foo.tmp of <__main__.Foo object at ...>>
Since tmp
is a function object, according to the documentation shown above, a bound method object is created and it expects an Object of Foo
to be the first parameter. So, tmp
is actually bound to Foo
as an instancemethod
. When you invoke go
like Foo().go()
, it internally calls tmp
like this
Foo.func(self, 0.0)
which is effectively
tmp(self, 0.0)
That is why you are getting the following error
TypeError: tmp() takes exactly 1 argument (2 given)
- How can I safely save an ordinary function in a class/instance attribute and call it later from an instance method?
Solution 1:
Quoting Python documentation again,
It is also important to note that user-defined functions which are attributes of a class instance are not converted to bound methods; this only happens when the function is an attribute of the class.
It means that, when you assign a user defined function to a class variable, the bound method construction will happen. But, if you assign it to an instance then it will not happen.
So, you can use this to your advantage, like this
import time
def tmp(stamp):
return time.gmtime(stamp)
class Foo(object):
def __init__(self):
self.func = tmp # Instance variable, not a class variable
def go(self):
return self.func(0.0)
print time.strftime('%Y', Foo().go())
Here, self.func
will translate to tmp(0.0)
, as there is no bound method construction happening.
Solution 2:
Use staticmethod
function like this
class Foo(object):
func = staticmethod(tmp)
def go(self):
# `return Foo.func(0.0)` This will also work
return self.func(0.0)
Now, self.func
would still refer tmp
. It is similar to defining your class like this
class Foo(object):
@staticmethod
def func(stamp):
return time.gmtime(stamp)
def go(self):
# `return Foo.func(0.0)` This will also work
return self.func(0.0)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With