I made a post here functions and class attributes (python)
When you define
a class attribute and gave it a function like this:
example 1
def add_age(cls,age):
cls.yrs_old = age
return cls
class Test:
age = add_age
a = Test()
a.age(5)
print(a.yrs_old)
self is automatically passed as the first argument of the add_age function.
However toying around with it more doing the same thing
but this time passing the function as an instance attribute like this:
example 2
def test_func(self):
self.class_attribute = "test"
class Test:
def __init__(self,func):
self.func = func
a = Test(test_func)
print(a.func())
Answers in the linked post stated that all functions in the class are automatically passed a self if the class is instantiated like this:
a = Test(test_func)
Now what's strange here is had I put test_func in a class attribute it works just like my very first example.
If you pass the function in the constructor/init like this:
def test_func(self):
self.class_attribute = "test"
class Test:
def __init__(self,func):
self.func = func
and call it like this:
a = Test(test_func)
print(a.func())
a.func is suddenly acting like a static method as opposed to example 1 where the function defined inside the class attribute becomes a regular class method.
What's going on?.
I thought all functions within a class are implicitly passed a self argument.
After the body of the class statement is evaluated, the metaclass wraps each function in a descriptor which takes care of the distinction between instance, class, and static methods. When you assign the function to an instance attribute, you bypass that machinery, so that the attribute refers to a plain function object.
From documentation -
Any function object that is a class attribute defines a method for instances of that class. It is not necessary that the function definition is textually enclosed in the class definition: assigning a function object to a local variable in the class is also ok.
This means that only methods that are assigned to classes are instance methods for the instances of the class.
Example -
>>> class A:
... def a(self):
... print("Hmm")
...
>>> b = A()
>>> b.a()
Hmm
>>> b.a
<bound method A.a of <__main__.A object at 0x006D13D0>>
But as soon as you assign a separate function object to the instance variable, it is no longer an instance method , since is is not defined at the class level, it is only defined for that particular instance , Example -
>>> def c():
... print("Hello")
...
>>> b.a = c
>>> b.a()
Hello
>>> b.a
<function c at 0x0017B198>
As you can see, when you directly assigned the function to the instance variable (instead of assigning it to class variable , it is now a normal instance attribute, that references a function object, and not an instance method.
You can also assign functions to class variables after the definition of class , and the instances would automatically get them as instance methods, Example -
>>> class A:
... def a(self):
... print("Hmm")
...
>>> def c(a):
... print("Hello - ", a)
...
>>> b = A()
>>> A.b = c
>>> b.b
<bound method A.c of <__main__.A object at 0x006D13D0>>
>>> b.b()
Hello <__main__.A object at 0x006D13D0>
Related
I have a setup that looks as following:
def docstring_formatter(func):
func.__doc__ = func.__doc__.format(class_name=func.__self__.__class__.__name__) #this does not work
return func
class A:
#docstring_formatter
def my_function(self):
"""I am a function of class {class_name}"""
print("test")
class B(A):
pass
docstring = getattr(B.my_function, "__doc__")
>>> AttributeError: 'function' object has no attribute '__self__'
I would like to access the actual class name that the instance of my_function belongs to. Since I am not instantiating the class I when I am using the help() function, the __self__ property is not instantiated yet and I can also not make use of the functools.wraps function. I would like to find a way to also extract the string B or B.my_function when being passed a my_function object that could either belong to A() or B().
Given the code:
class A:
def my_function(self):
print("test")
class B(A):
pass
b = B()
The my_function object has a self binding to the object you took it from, which is a B, so you can do:
>>> b.my_function.__name__
'my_function'
>>> b.my_function.__self__.__class__.__name__
'B'
I may be trying to do something that is outside of the realm of possibility here, but I figured I would ask first before abandoning hope. So here it goes...
I have 2 classes, A and B. Each class has an arbitrary number of functions. Class B will be instantiated somewhere in Class A and Class A will utilize one of Class B functions via that instantiation. A function in Class B will need to refer to one or more of Class A's functions using it's current instantiation data of Class A.
Class A
#!/usr/bin/python
from classB import classB
class classA(object):
def Apple(self):
print("Inside Apple")
b = classB()
b.Banana()
b.bar()
def foo(self):
print("foo inside apple")
a = classA()
a.Apple()
Class B:
#!/usr/bin/python
import inspect
class classB(object):
def Banana(self):
print("Inside banana")
def bar(self):
print("bar inside banana")
'''
The following lines just show I can get the names of the
calling class and methods.
'''
stack = inspect.stack()
the_class = stack[1][0].f_locals["self"].__class__
the_method = stack[1][0].f_code.co_name
print("Caller Class: {}".format(the_class))
print("Caller Method: {}".format(the_method))
function_name = 'foo'
if hasattr(the_class, function_name):
print("Class {} has method {}".format(the_class,
function_name))
getattr(the_class, function_name)()
I get the following error:
getattr(the_class, function_name)()
TypeError: unbound method foo() must be called with classA instance as first argument (got nothing instead)
Thanks!
As the error suggests, you must build an object of classA (i.e. the_class) before calling getattr on it.
objA = the_class()
But taking a step back, why don't you just pass class A to class B while initializing it?
b = classB(self)
That will allow you to access the exact method of class A that you need.
Else, if method 'foo' in class A is supposed to be a static method, make it so by using #staticmethod decorator.
I have two methods, one for the individual Instance, and one for every Instance in that class:
class MasterMatches(models.Model):
#classmethod
def update_url_if_any_matches_has_one(cls):
# apply to all instances, call instance method.
def update_url_if_any_matches_has_one(self):
# do something
Should I name these the same? Or, what is a good naming convention here?
The question of using the same names can be clarified by understanding how decorators work.
#dec
def foo(x):
print(x)
translates to
def foo(x):
print(x)
foo = dec(foo)
In your example the decorator syntax can be expanded to
class MasterMatches(models.Model):
def update_url_if_any_matches_has_one(cls):
# apply to all instances, call instance method.
update_url_if_any_matches_has_one = classmethod(update_url_if_any_matches_has_one)
def update_url_if_any_matches_has_one(self):
# do something
The former implementation of update_url_if_any_matches_has_one will be overwritten by the latter.
Usually use self declaration style. #classmethod use only if method not works with class instance fields.
Function decorated as #classmethod takes the first argument is the class type, while normal method takes instance of object.
class A:
#classmethod
def a(cls):
print(cls)
def b(self):
print(self)
a = A()
a.a()
a.b()
# Output:
# <class '__main__.A'>
# <__main__.A object at 0x03FC5DF0>
It can be useful if you have a static class fields. The to access therm you don't need explicitly specify the class name. But you don't get access to instance fields. Example:
class A:
field = 1
#classmethod
def a(cls):
print(cls.field)
def b(self):
self.field = 2
print(self.field, A.field)
a = A()
a.a()
a.b()
# Outputs:
# 1
# 2 1
assume following class definition:
class A:
def f(self):
return 'this is f'
#staticmethod
def g():
return 'this is g'
a = A()
So f is a normal method and g is a static method.
Now, how can I check if the funcion objects a.f and a.g are static or not? Is there a "isstatic" funcion in Python?
I have to know this because I have lists containing many different function (method) objects, and to call them I have to know if they are expecting "self" as a parameter or not.
Lets experiment a bit:
>>> import types
>>> class A:
... def f(self):
... return 'this is f'
... #staticmethod
... def g():
... return 'this is g'
...
>>> a = A()
>>> a.f
<bound method A.f of <__main__.A instance at 0x800f21320>>
>>> a.g
<function g at 0x800eb28c0>
>>> isinstance(a.g, types.FunctionType)
True
>>> isinstance(a.f, types.FunctionType)
False
So it looks like you can use types.FunctionType to distinguish static methods.
Your approach seems a bit flawed to me, but you can check class attributes:
(in Python 2.7):
>>> type(A.f)
<type 'instancemethod'>
>>> type(A.g)
<type 'function'>
or instance attributes in Python 3.x
>>> a = A()
>>> type(a.f)
<type 'method'>
>>> type(a.g)
<type 'function'>
To supplement the answers here, in Python 3 the best way is like so:
import inspect
class Test:
#staticmethod
def test(): pass
isstatic = isinstance(inspect.getattr_static(Test, "test"), staticmethod)
We use getattr_static rather than getattr, since getattr will retrieve the bound method or function, not the staticmethod class object. You can do a similar check for classmethod types and property's (e.g. attributes defined using the #property decorator)
Note that even though it is a staticmethod, don't assume it was defined inside the class. The method source may have originated from another class. To get the true source, you can look at the underlying function's qualified name and module. For example:
class A:
#staticmethod:
def test(): pass
class B: pass
B.test = inspect.getattr_static(A, "test")
print("true source: ", B.test.__qualname__)
Technically, any method can be used as "static" methods, so long as they are called on the class itself, so just keep that in mind. For example, this will work perfectly fine:
class Test:
def test():
print("works!")
Test.test()
That example will not work with instances of Test, since the method will be bound to the instance and called as Test.test(self) instead.
Instance and class methods can be used as static methods as well in some cases, so long as the first arg is handled properly.
class Test:
def test(self):
print("works!")
Test.test(None)
Perhaps another rare case is a staticmethod that is also bound to a class or instance. For example:
class Test:
#classmethod
def test(cls): pass
Test.static_test = staticmethod(Test.test)
Though technically it is a staticmethod, it is really behaving like a classmethod. So in your introspection, you may consider checking the __self__ (recursively on __func__) to see if the method is bound to a class or instance.
I happens to have a module to solve this. And it's Python2/3 compatible solution. And it allows to test with method inherit from parent class.
Plus, this module can also test:
regular attribute
property style method
regular method
staticmethod
classmethod
For example:
class Base(object):
attribute = "attribute"
#property
def property_method(self):
return "property_method"
def regular_method(self):
return "regular_method"
#staticmethod
def static_method():
return "static_method"
#classmethod
def class_method(cls):
return "class_method"
class MyClass(Base):
pass
Here's the solution for staticmethod only. But I recommend to use the module posted here.
import inspect
def is_static_method(klass, attr, value=None):
"""Test if a value of a class is static method.
example::
class MyClass(object):
#staticmethod
def method():
...
:param klass: the class
:param attr: attribute name
:param value: attribute value
"""
if value is None:
value = getattr(klass, attr)
assert getattr(klass, attr) == value
for cls in inspect.getmro(klass):
if inspect.isroutine(value):
if attr in cls.__dict__:
bound_value = cls.__dict__[attr]
if isinstance(bound_value, staticmethod):
return True
return False
Why bother? You can just call g like you call f:
a = A()
a.f()
a.g()
i have a function which is a class method, and i want to test a attribute of the class which may or may not be None, but will exist always.
class classA():
def __init__(self, var1, var2 = None):
self.attribute1 = var1
self.attribute2 = var2
#classmethod
def func(self,x):
if self.attribute2 is None:
do something
i get the error
AttributeError: class classA has no attribute 'attributeB'
when i access the attribute like i showed but if on command line i can see it works,
x = classA()
x.attribute2 is None
True
so the test works.
if i remove the #classmethod decorator from func, the problem disapears.
if i leave the #classmethod decorator, it only seems to affect variables which are supplied default values in the super-class's constructor.
whats going on in the above code?
There is a difference between class attributes and instance attributes. A quick demonstration would be this:
>>> class A(object):
... x=4
... def __init__(self):
... self.y=2
>>> a=A() #a is now an instance of A
>>> A.x #Works as x is an attribute of the class
2: 4
>>> a.x #Works as instances can access class variables
3: 4
>>> a.y #Works as y is an attribute of the instance
4: 2
>>> A.y #Fails as the class A has no attribute y
Traceback (most recent call last):
File "<pyshell#9>", line 1, in <module>
A.y #Fails as the class A has no attribute y
AttributeError: type object 'A' has no attribute 'y'
>>>
Now, when a method of a class is decorated with classmethod, that signals that it does not take an instance, but takes the class itself as the parameter. Thus, conventionally we name the first argument cls, and not self. In your code, classA has no attributes, and so trying to access attribute2 fails. This difference can be shown with the below code:
>>> class B(object):
... x=2
... def __init__(self):
... self.x=7
... def pr1(self):
... print self.x
... #classmethod
... def pr2(cls):
... print cls.x
>>> b=B()
>>> B.x
2
>>> b.x
7
>>> b.pr1()
7
>>> b.pr2()
2
>>> B.pr2()
2
I might not have been clear enough, so if you are still confused just search classmethod or new-style classes and read up a bit on this.
You should first test to see if you HAVE the attribute with hasattr() or somesuch.
class classA(superClass):
def func(self,x):
if not hasattr(self, "attributeB") or self.attributeB is None:
do somthing
You may also want to make sure that the sub-class is calling the constructor method from the parent class. That attribute is obviously getting assigned after you're referencing it. So make sure the class is properly constructed with
parentclassName.__init__(self, ... )
self in an instance method is the instance. self (or more traditionally, cls) in a class method is the class. Attributes bound on an instance are not visible on the class. The only way to make this work would be to pass the instance to the class method, at which point you may as well just make it an instance method.
The two attributes are instance attributes, not class attributes. The class method is trying to reference class attributes. Neither your attribute1 nor your attribute2 exist on the class: they exist on the instance.
I don't know how to fix this, but this is the source of the problem.
(Verified by changing attribute2 to attribute1 in func.)
So the question should really be, "How to reference instance attributes within a class method?"