Is there any universal way to get the attributes of a class by class name and instance?
class A:
def __init__(self):
self.prop = 1
a = A()
for attr, value in a.__dict__.items():
print(attr, value) # prop, 1
.
class A:
def __init__(self):
self.prop = 1
for attr, value in A.__dict__.items():
print(attr, value)
#__dict__, __doc__, __init__, __module__, __weakref__
Why the last example returns dir attibutes why the results differ?
__dict__, __doc__, __module__, ... are actually present in a class, even you haven't created them. They are 'built-in'.
So it's normal that dir shows them to you.
__dict__ attribute in an instance stores instance attributes.
class A:
def __init__(self):
self.prop = 1
a = A()
for attr, value in a.__dict__.items():
print(attr, value)
This shows instance attributes. And there is just one instance attribute - prop (self.prop = 1)
for attr, value in A.__dict__.items():
And this gets class attributes. prop was added to an instance, so it's not here.
See http://docs.python.org/library/stdtypes.html#special-attributes
To get from an object all attributes, including class attributes, base class attributes, use inspect.getmembers
Related
class Remote:
aa=7
def __init__(self):
self.name="Lenovo"
self.b=self.Battery()
print("this is outer",self.b.t)
class Battery:
def __init__(self):
self.name="Hp"
self.t="df"
self.c=self.Cover()
class Cover:
def __init__(self):
self.name="Arplastic"
c1=Remote()
I knew today about inner class but i don't know how to i access properties and methods of outer class into inner class please let me know anyone.
Change the constructor(s) of the inner class(es) to accept a parent argument and have the creating instance pass itself to it:
class Remote:
aa=7
def __init__(self):
self.name="Lenovo"
self.b=self.Battery(self)
print("this is outer",self.b.t)
class Battery:
def __init__(self,parent):
self.name="Hp"
self.t="df"
self.c=self.Cover(self)
self.parent=parent
class Cover:
def __init__(self,parent):
self.name="Arplastic"
self.parent=parent
c1=Remote()
print(c1.b.c.parent.parent.name) # prints 'Lenovo'
One approach is to make a metaclass that automatically creates self.parent attributes for nested classes. Note that there is a trade-off between readability and boilerplate here - many programmers would rather you just manually pass parents as arguments and add them to __init__ methods. This is more fun though, and there is something to be said for having less cluttered code.
Here is the code:
import inspect
def inner_class(cls):
cls.__is_inner_class__ = True
return cls
class NestedClass(type):
def __new__(metacls, name, bases, attrs, parent=None):
attrs = dict(attrs.items())
super_getattribute = attrs.get('__getattribute__', object.__getattribute__)
inner_class_cache = {}
def __getattribute__(self, attr):
val = super_getattribute(self, attr)
if inspect.isclass(val) and getattr(val, '__is_inner_class__', False):
if (self, val) not in inner_class_cache:
inner_class_cache[self, val] = NestedClass(val.__name__, val.__bases__, val.__dict__, parent=self)
return inner_class_cache[self, val]
else:
return val
attrs['__getattribute__'] = __getattribute__
attrs['parent'] = parent
return type(name, bases, attrs)
class Remote(metaclass=NestedClass):
aa = 7
def __init__(self):
self.name = "Lenovo"
self.b = self.Battery()
print("this is outer", self.b.t)
#inner_class
class Battery:
def __init__(self):
self.name = "Hp"
self.t = "df"
self.c = self.Cover()
#inner_class
class Cover:
def __init__(self):
self.name = "Arplastic"
print(f'{self.parent=}, {self.parent.parent=}')
c1 = Remote()
print(f'{c1.b.c.parent.parent is c1=}')
print(f'{isinstance(c1.b, c1.Battery)=}')
Output:
self.parent=<__main__.Battery object at 0x7f11e74936a0>, self.parent.parent=<__main__.Remote object at 0x7f11e7493730>
this is outer df
c1.b.c.parent.parent is c1=True
isinstance(c1.b, c1.Battery)=True
The way this works is by storing the parent as a class attribute (which is None by default), and replacing the __getattribute__ method so that all inner classes are replaced with NestedClasses with the parent attribute correctly filled in.
The inner_class decorator is used to mark a class as an inner class by setting the __is_inner_class__ attribute.
def inner_class(cls):
cls.__is_inner_class__ = True
return cls
This is not strictly necessary if all attributes that are classes should be treated as inner classes, but it's good practice to do something like this to prevent Bar.foo being treated as an inner class in this example:
class Foo:
pass
class Bar(metaclass=NestedClass):
foo = Foo
All the NestedClass metaclass does is take the description of the class and modify it, adding the parent attribute:
class NestedClass(type):
def __new__(metacls, name, bases, attrs, parent=None):
attrs = dict(attrs.items())
...
attrs['parent'] = parent
return type(name, bases, attrs)
...and modifying the __getattribute__ method. The __getattribute__ method is a special method that gets called every time an attribute is accessed. For example:
class Foo:
def __init__(self):
self.bar = "baz"
def __getattribute__(self, item):
return 1
foo = Foo()
# these assert statements pass because even though `foo.bar` is set to "baz" and `foo.remote` doesn't exist, accessing either of them is the same as calling `Foo.__getattribute(foo, ...)`
assert foo.bar == 1
assert foo.remote == 1
So, by modifying the __getattribute__ method, you can make accessing self.Battery return a class that has its parent attribute equal to self, and also make it into a nested class:
class NestedClass(type):
def __new__(metacls, name, bases, attrs, parent=None):
attrs = dict(attrs.items())
# get the previous __getattribute__ in case it was not the default one
super_getattribute = attrs.get('__getattribute__', object.__getattribute__)
inner_class_cache = {}
def __getattribute__(self, attr):
# get the attribute
val = super_getattribute(self, attr)
if inspect.isclass(val) and getattr(val, '__is_inner_class__', False):
# if it is an inner class, then make a new version of it using the NestedClass metaclass, setting the parent attribute
if (self, val) not in inner_class_cache:
inner_class_cache[self, val] = NestedClass(val.__name__, val.__bases__, val.__dict__, parent=self)
return inner_class_cache[self, val]
else:
return val
attrs['__getattribute__'] = __getattribute__
attrs['parent'] = parent
return type(name, bases, attrs)
Note that a cache is used to ensure that self.Battery will always return the same object every time rather than re-making the class every time it is called. This ensures that checks like isinstance(c1.b, c1.Battery) work correctly, since otherwise c1.Battery would return a different object to the one used to create c1.b, causing this to return False, when it should return True.
And that's it! You can now enjoy nested classes without boilerplate!
Is it possible to replace a property of an object with 'normal' attribute?
I need this because when I access the attribute for the first, I want the value to be generated by the property. But I no longer need the property afterward:
class A(object):
#property
def x(self):
self.x = "toto" # Replace property, fail because no setter
return self.x
a = A()
print a.x # "toto"
a.x = "tata"
I know I can store the value in a second attribute like _xand check in the property if _x exist but I want to know if it's possible the replace the property itself.
To bypass the lack of a setter, you'd have to directly manipulate the instance __dict__ dictionary. However, you can't do what you want with a regular property object, because it is a data descriptor. Attribute access will always give a data descriptor priority over instance attributes.
You'd have to create a custom descriptor instead, one that doesn't define a __set__ or __delete__ method:
class CachingProperty(object):
def __init__(self, fget):
self.name = fget.__name__
self.fget = fget
def __get__(self, instance, owner):
if instance is None:
return self
value = self.fget(instance)
instance.__dict__[self.name] = value
return value
This descriptor also takes care of setting the value directly in the instance __dict__ attribute, thus creating an instance attribute.
Use the above class instead of property:
class A(object):
#CachingProperty
def x(self):
return "toto"
Demo, showing that the getter method is only called once:
>>> class Demo(object):
... #CachingProperty
... def foo(self):
... print("Calling the foo property")
... return "bar"
...
>>> d = Demo()
>>> d.foo
Calling the foo property
'bar'
>>> d.foo
'bar'
>>> vars(d)
{'foo': 'bar'}
Does Python have a mechanism for class constructors, i.e. a function that is called whenever the class is first referenced (as opposed to when an instance of that object is created)? I know this exists in some other languages, but I haven't come across it in Python.
Basically, I would like to initialise some static attributes in that function. I put an example below of what I would expect. Of course, the example returns None, but I would like it return 'foo'.
class T:
arg = None
def __class_constructor__():
T.arg = 'foo'
print(T.arg) # returns None
To avoid confusion: I am well aware of the object constructor, but that's not what I want, because it is only called once the first object is created, not before:
class T:
arg = None
def __init__(self):
type(self).arg = 'foo'
print(T.arg) # returns None
obj = T()
print(T.arg) # returns 'foo'
You can use a class decorator:
def add_arg(cls):
if not hasattr(cls, "arg"):
cls.arg = 'foo'
return cls
#add_arg
class T(object):
pass
Or a custom metaclass:
class WithArg(type):
def __new__(meta, name, bases, attrs):
cls = type.__new__(meta, name, bases, attrs)
if not hasattr(cls, "arg"):
cls.arg = "foo"
return cls
# python 2
class T(object):
__metaclass__ = WithArg
# python 3
class T(metaclass=WithArg):
pass
But as others already mention this won't give you much more than plainly setting the class attribute in the class statement.
NB : if you want a computed attribute on the class itself, you'll have to either set it as a property on a custom metaclass
class WithProp(type):
#property
def arg(cls):
return "foo"
class T(object):
__metaclass__ = WithProp
T.arg
=> 'foo'
But arg will only be available on the class object itself, not on it's instances:
T().arg
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'T' object has no attribute 'arg'
or write your own custom descriptor:
class ArgDescriptor(object):
def __get__(self, obj, cls=None):
return 42
class T(object):
arg = ArgDescriptor()
T.arg
=> 42
T().arg
=> 42
You simply have to initialise the class variable when declaring it within the class
class T:
arg = 'foo' #this initialises the class instance
def __init__(self):
self.arg = 'bar' #this initialises the object instance
print(T.arg) # class instance returns 'foo'
obj = T()
print(T.arg) # class instance still returns 'foo'
print(obj.arg) # object instance returns 'bar'
I create a static_init decorator which calls a static_init class method if it exists. This static_init class method will run when the class decorator is evaluated which is when the class is defined - so not quite when the class is first referenced - but it is analogous to static initialization in other languages like Java.
Here is the decorator and example of how to use it to initialize a class variable on an enum class:
# pylint: disable=missing-docstring,no-member
import enum
def static_init(cls):
if getattr(cls, "static_init", None):
cls.static_init()
return cls
#static_init
class SomeEnum(enum.Enum):
VAL_A = enum.auto()
VAL_B = enum.auto()
VAL_C = enum.auto()
VAL_D = enum.auto()
#classmethod
def static_init(cls):
text_dict = {}
setattr(cls, 'text_dict', text_dict)
for value in cls:
text_dict[value.name.lower().replace("_", " ").title()] = value
def test_static_init():
assert SomeEnum.text_dict["Val A"] == SomeEnum.VAL_A
assert SomeEnum.text_dict["Val B"] == SomeEnum.VAL_B
assert SomeEnum.text_dict["Val C"] == SomeEnum.VAL_C
assert SomeEnum.text_dict["Val D"] == SomeEnum.VAL_D
I am trying to understand descriptors better.
I don't understand why in the foo method the descriptors __get__ method doesn't get called.
As far as I understand descriptors the __get__ method always get called when I access the objects attribute via dot operator or when I use __getattribute__().
According to the Python documentation:
class RevealAccess(object):
def __init__(self, initval=None, name='var'):
self.val = initval
self.name = name
def __get__(self, obj, objtype):
print('Retrieving', self.name)
return self.val
def __set__(self, obj, val):
print('Updating', self.name)
self.val = val
class MyClass(object):
x = RevealAccess(10, 'var "x"')
y = 5
def foo(self):
self.z = RevealAccess(13, 'var "z"')
self.__getattribute__('z')
print(self.z)
m = MyClass()
m.foo()
m.z # no print
m.x # prints var x
z is an attribute on the instance, not on the class. The descriptor protocol only applies to attributes retrieved from a class.
From the Descriptor HOWTO:
For objects, the machinery is in object.__getattribute__() which transforms b.x into type(b).__dict__['x'].__get__(b, type(b)).
and in the Implementing Descriptors section of the Python Data Model:
The following methods only apply when an instance of the class containing the method (a so-called descriptor class) appears in an owner class (the descriptor must be in either the owner’s class dictionary or in the class dictionary for one of its parents).
Your m.z cannot be found in the class dict; type(m).__dict__['z'] does not exist; it is found in m.__dict__['z'] instead. Here m is the instance and the owner class is MyClass, and z does not appear in the owner class dictionary.
class test(object):
def __init__(self, a = 0):
test.a = a
t = test()
print test.a ## obviously we get 0
''' ====== Question ====== '''
print test.somethingelse ## I want if attributes not exist, return None. How to do that?
First of all, you are adding the variable to the class test.a = a. You should be adding it to the instance, self.a = a. Because, when you add a value to the class, all the instances will share the data.
You can use __getattr__ function like this
class test(object):
def __init__(self, a = 0):
self.a = a
def __getattr__(self, item):
return None
t = test()
print t.a
print t.somethingelse
Quoting from the __getattr__ docs,
Called when an attribute lookup has not found the attribute in the usual places (i.e. it is not an instance attribute nor is it found in the class tree for self). name is the attribute name.
Note: The advantage of __getattr__ over __getattribute__ is that, __getattribute__ will be called always, we have to handle manually even if the current object has the attribute. But, __getattr__ will not be called if the attribute is found in the hierarchy.
You are looking for the __getattribute__ hook. Something like this should do what you want:
class test(object):
def __init__(self, a = 0):
self.a = a
def __getattribute__(self, attr):
try:
return object.__getattribute__(self, attr)
except AttributeError:
return None