Can't find classmethod over class name - python

I am trying to invoke classmethod over classname .AttributeError problem occurs
When I use #singleton ,I can't run with classname.functionname .It's must be classname().functionname
Why does this happen?
def singleton(cls):
'''
单例
:param cls:
:return:
'''
_instance = {}
def _singleton(*args, **kargs):
if cls not in _instance:
_instance[cls] = cls(*args, **kargs)
# print(type(_instance[cls])) <class '__main__.Coco'>
return _instance[cls]
return _singleton
#singleton
class Coco():
# def __new__(cls, *args, **kwargs):
# if not hasattr(Coco, "_instance"):
# if not hasattr(Coco, "_instance"):
# Coco._instance = object.__new__(cls)
# print(type(Coco._instance))
# return Coco._instance
def __init__(self):
print('coco')
#classmethod
def get_info(cls):
print('coco is 18 ages old')
# print(Coco().get_info())
print(Coco.get_info())
Exception
Traceback (most recent call last):
File "/Users/coco/Automation/AutoTestRes/scripts/python/coco.py", line 36, in <module>
print(Coco.get_info())
AttributeError: 'function' object has no attribute 'get_info'

When you use a decorator in Python, like this:
#decorator_name
class class_name:
...
..., this is equivalent to doing this:
class class_name:
...
class_name = decorator_name(class_name)
This means that the value of the variable class_name is no longer necessarily a class, but instead it is whatever the return value of decorator_name is.
In your case, the class decorator singleton returns the function _singleton, not the actual class. So when you say:
print(Coco.get_info())
..., this is the same as saying:
print(_singleton.get_info())
...within the function.
Therefore, you get an AttributeError, because the function, which now has the name Coco, does not have that attribute.
To access the attribute of the class, you need to run the function because this will return an instance of the class, which will have the attribute.
It is no longer possible to access the class itself from the global scope.

Related

How does __setattr__ and __getattribute__ interact in Python?

class Test():
def __init__(self,age):
self.age=age
def __getattribute__(self,attribute):
print("Initializing getattribute")
return 6
def __setattr__(self,attribute,value):
print("Initializing setattr")
return object.__setattr__(self,attribute,value)
test=Test(4)
test.age
print(test.age)
From the code above the result is :
Initializing setattr
Initializing getattribute
Initializing getattribute
6
I understand where each dunder method is called, but what do they really do?In the previous example getattribute dictate the attribute value and if I delete the line :
return object.__setattr__(self,attribute,value)
Nothing changes.
So what does __setattr__ do?
__getattribute__ is called before any other attempt is made to access an attribute. No matter what __setattr__ does, test.age is handled by test.__getattribute__("age"), which returns 6 whether or not there is an attribute named age.
If you get rid of __getattribute__:
class Test():
def __init__(self,age):
self.age=age
def __setattr__(self,attribute,value):
print("Initializing setattr")
return object.__setattr__(self,attribute,value)
test=Test(4)
test.age
print(test.age)
The class behaves normally, setting test.age to 4. If you further get rid of the call to object.__setattr__, then you'll get an AttributeError because self.age = age will never actually create or set the age attribute; it just prints the initialization message and returns:
class Test():
def __init__(self,age):
self.age=age
def __setattr__(self,attribute,value):
print("Initializing setattr")
test=Test(4)
test.age
print(test.age)
results in
Initializing setattr
Traceback (most recent call last):
File "/Users/chepner/tmp.py", line 11, in <module>
test.age
AttributeError: 'Test' object has no attribute 'age'

Decorated class looses acces to its attributes

I implemented a decorator that worked like a charm until I added attributes to the decorated class. When I instantiate the class, it cannot acces the calss attributes. Take the following minimal working example :
from module import specialfunction
class NumericalMathFunctionDecorator:
def __init__(self, enableCache=True):
self.enableCache = enableCache
def __call__(self, wrapper):
def numericalmathfunction(*args, **kwargs):
func = specialfunction(wrapper(*args, **kwargs))
"""
Do some setup to func with decorator arguments (e.g. enableCache)
"""
return numericalmathfunction
#NumericalMathFunctionDecorator(enableCache=True)
class Wrapper:
places = ['home', 'office']
configs = {
'home':
{
'attr1': 'path/at/home',
'attr2': 'jhdlt'
},
'office':
{
'attr1': 'path/at/office',
'attr2': 'sfgqs'
}
}
def __init__(self, where='home'):
# Look for setup configuration on 'Wrapper.configs[where]'.
assert where in Wrapper.places, "Only valid places are {}".format(Wrapper.places)
self.__dict__.update(Wrapper.configs[where])
def __call__(self, X):
"""Do stuff with X and return the result
"""
return X ** 2
model = Wrapper()
When I instantiate the Wrapper class (#1), I get the following error :
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-5-a99bd3d544a3> in <module>()
15 assert where in Wrapper.places, "Only valid places are {}".format(Wrapper.places)
16
---> 17 model = Wrapper()
<ipython-input-5-a99bd3d544a3> in numericalmathfunction(*args, **kwargs)
5 def __call__(self, wrapper):
6 def numericalmathfunction(*args, **kwargs):
----> 7 func = wrapper(*args, **kwargs)
8 return numericalmathfunction
9
<ipython-input-5-a99bd3d544a3> in __init__(self, where)
13 def __init__(self, where='home'):
14 # Look for setup configuration on 'Wrapper.configs[where]'.
---> 15 assert where in Wrapper.places, "Only valid places are {}".format(Wrapper.places)
16
17 model = Wrapper()
AttributeError: 'function' object has no attribute 'places'
I guess that with the decorator, Wrapper becomes a function that looses acces to its attributes...
Any ideas of how I can solve this ? Maybe there is a workaround
You replaced Wrapper (which was a class) with the numericalmathfunction function object. That object doesn't have any of the class attributes, no.
In essence, the decorator does this:
class Wrapper:
# ...
Wrapper = NumericalMathFunctionDecorator(enableCache=True)(Wrapper)
so whatever the NumericalMathFunctionDecorator.__call__ method returns has now replaced the class; all references to Wrapper now reference that return value. And when you use the name Wrapper in the __init__ method, you are referencing that global, not the original class.
You can still access the current class with type(self), or just reference those attributes via self (where the name lookup falls through to the class):
def __init__(self, where='home'):
# Look for setup configuration on 'Wrapper.configs[where]'.
assert where in self.places, "Only valid places are {}".format(self.places)
self.__dict__.update(self.configs[where])
or
def __init__(self, where='home'):
# Look for setup configuration on 'Wrapper.configs[where]'.
cls = type(self)
assert where in cls.places, "Only valid places are {}".format(cls.places)
self.__dict__.update(cls.configs[where])
In both cases you can end up with referencing an attribute on a subclass if you ever did subclass Wrapper (which you cannot do in this case anyway as you would have to fish the class out of the decorator closure).
Alternatively, you could store the original class as an attribute on the returned function:
def __call__(self, wrapper):
def numericalmathfunction(*args, **kwargs):
func = specialfunction(wrapper(*args, **kwargs))
"""
Do some setup to func with decorator arguments (e.g. enableCache)
"""
numericalmathfunction.__wrapped__ = wrapper
return numericalmathfunction
then use that reference in your __init__:
def __init__(self, where='home'):
# Look for setup configuration on 'Wrapper.configs[where]'.
cls = Wrapper
while hasattr(cls, '__wrapped__'):
# remove any decorator layers to get to the original
cls = cls.__wrapped__
assert where in cls.places, "Only valid places are {}".format(cls.places)
self.__dict__.update(cls.configs[where])

Access base class variable from metaclass

I am trying to read base class variable from metaclass to override class variable using this code:
class TypeBaseMeta(type):
def __new__(cls, name, bases, namespace, **kwds):
for base in bases:
namespace['__validators__'] = base['__validators__'] + namespace['__validators__']
return type.__new__(cls, name, bases, namespace, **kwds)
class TypeBase(metaclass=TypeBaseMeta):
__validators__ = ('presence')
def __init__(self, *args, **kwargs):
pass
def validate_presence(self, flag):
if self.data:
return True
class String(TypeBase):
__validators__ = ('length')
def validate_length(self, range):
if len(self.data) in range(*range):
return True
but I got this error:
Traceback (most recent call last):
File "types.py", line 18, in <module>
class String(TypeBase):
File "types.py", line 4, in __new__
namespace['__validators__'] = base['__validators__'] + namespace['__validators__']
TypeError: 'TypeBaseMeta' object is not subscriptable
I know that subscriptable object must have __getitem__() and behave like dictionaries and list but I have no idea what is causing this error.
__validators__ is an attribute of the superclass, not a dict item, so it should be accessed with base.__validators__. (That is, change base['__validators__'] to base.__validators__. Don't change namespace['__validators__'].)
The reason you access the attribute of the current class with namespace['__validators__'] is because that class doesn't exist yet (it is being created by the metaclass). Right now all you have is a dict of its attributes. But the superclass (base) was already created, and is a real class whose attributes are accessed in the normal way, with ..
As Dunes points out in a comment, your code has another problem, which is that you should be writing ('presence',) and ('length',) for your validators, to create tuples. Otherwise they are just strings, and the subclass's __validators__ will be set to the single string 'presencelength'.
The __validators__ variable in the superclasses is not acessible as if it were a dictionary - you have to fecth it from it's __dict__ attribute, or use getattr.
- namespace['__validators__'] = base['__validators__'] + namespace['__validators__']
+ namespace['__validators__'] = base.__dict__.get('__validators__', ()) + namespace['__validators__']

What is the hidden argument being passed to my `MethodType`?

I recently came across this recipe for making a "weakmethod" and thought it was the bees' knees; but there seems to be a mystery argument being passed to the resulting MethodType function that i can't seem to find:
from weakref import proxy
from types import MethodType
class Foo(object):
def __getattribute__(self, name):
if name.startswith('foo_'):
return MethodType(super(Foo, self).__getattribute__(name), proxy(self), self.__class__)
else:
return super(Foo, self).__getattribute__(name)
class Bar(Foo):
def my_func(self, a, b):
print a, b
def foo_my_func(self, a, b):
print 'FF Victory Theme'
>>> bar = Bar()
>>> bar.my_func(1, 2)
1 2
>>> weakmethod = bar.foo_my_func
>>> weakmethod(2, 3) # Or `bar.foo_my_func(2, 3)`
Traceback (most recent call last):
File "<pyshell#160>", line 1, in <module>
weakmethod(2, 3)
TypeError: foo_my_func() takes exactly 3 arguments (4 given)
What is this 4th argument that's being passed?
You used super(Foo, self).__getattribute__(name) to access the foo_my_func method. This already returns a MethodType object. You then wrap this object again.
So your returned object passes in proxy(self) to the wrapped method, which passes in another self argument. You started with a, b, and end up with self, proxy(self), a, b.
The recipe you linked to uses a decorator instead; this decorator is executed at class definition time, and wraps the function object. It is itself a descriptor, so it handles all the wrapping directly.
You'll want to either unwrap the result of super(Foo, self).__getattribute__(name) or don't use __getattribute__ at all.
Unwrapping can be done with accessing the __func__ attribute on a method:
class Foo(object):
def __getattribute__(self, name):
attr = super(Foo, self).__getattribute__(name)
if name.startswith('foo_'):
return MethodType(attr.__func__, proxy(self), self.__class__)
return attr
Not using __getattribute__ is done by just accessing the __dict__ mapping on the class directly:
class Foo(object):
def __getattribute__(self, name):
if name.startswith('foo_'):
for cls in type(self).__mro__:
if name in cls.__dict__:
return MethodType(cls.__dict__[name], proxy(self), self.__class__)
return super(Foo, self).__getattribute__(name)
where type(self).__mro__ lets you iterate over the class and it's base classes in method resolution order to manually search for the method.

Python inherited type variables

I suppose i'm misunderstand how type inheritance work in python.
While i'm defining variable inside Parent class, any Child class inherited in parent
referencing same variable from parent.
class Parent(object):
store = dict()
class ChildA(Parent):
pass
class ChildB(Parent):
pass
ChildA.store['key1'] = 'val'
ChildB.store['key2'] = 'val'
print ChildB.store['key1'] == ChildA.store['key2']
What i'm trying to achieve is store dictionary instance to be created in every Child class inherited from Parent. So referencing ChildB.store['key1'] would raise KeyError
I have tried to use __new__ to create dictionary instance while type is creating:
class NewParent(object):
def __new__(cls, *args, **kwargs):
rv = super(NewParent,cls).__new__(cls, *args, **kwargs)
rv.store = dict()
return rv
But it's seems like __new__ running only before instantiating Child class, so referencing variable via type (e.g. Child.store is raising AttributeError)
So is there any way to achieve behavior i want?
You want to use a metaclass, which lets you initialize a class definition sort of like how a constructor lets you initalize an instance. For more details, see http://eli.thegreenplace.net/2011/08/14/python-metaclasses-by-example/.
Example:
#!/usr/bin/env python2
class ParentMeta(type):
def __new__(meta, name, bases, dct):
dct['store'] = dict()
return super(ParentMeta, meta).__new__(meta, name, bases, dct)
class Parent(object):
__metaclass__ = ParentMeta
class ChildA(Parent):
pass
class ChildB(Parent):
pass
ChildA.store['key1'] = 'val'
ChildB.store['key2'] = 'val'
print ChildB.store['key1'] == ChildA.store['key2']
will result in
Traceback (most recent call last):
File "test.py", line 20, in <module>
print ChildB.store['key1'] == ChildA.store['key2']
KeyError: 'key1'

Categories

Resources