Can a Python class attribute be of the same type?
Something like:
class X:
def __init__(self, inputStr):
a = 0
b = X("Hello")
I am not sure what is the use case you need it for or whether it was a theoretical question. But you might check out class inheritance if that is what you need.
class X:
def __init__(self, inputStr):
a = 0
self.inputStr = inputStr
class Y(X):
pass
obj = Y('Hello')
print(obj.inputStr)
Related
I have class with a nested/inner class. How to get the class variable moduleId within class B's init method to pass it into ExtraSuperClass?
class A:
moduleId = 1
class B(ExtraSuperClass):
def __init__(self):
super().__init__(moduleId)
def __init__(self):
n = self.B()
In example you provided you could access it like this:
A.moduleId
Also, you can't call B like that, you should do it like this:
self.B()
Was wondering if there was a way to set a class attribute to a specific instance from within the class definition. For example,
class Value:
def __init__(self, x):
self.x = x
# Something like
# half = Value(0.5)
>>> Value.half.x
0.5
>>> Value.half.half.x
0.5
I'm also aware I can easily set it outside the class that seems a bit more bulky and error prone, like this
class Value:
def __init__(self, x):
self.x = x
Value.half = Value(0.5)
>>> Value.half.x
0.5
>>> Value.half.half.x
0.5
No. At the time the body of the class is being evaluated, the class doesn't yet exist. A class statement is a declarative syntax for calling a metaclass:
class Value:
def __init__(self, x):
self.x = x
is roughly equivalent to
def init(self, x):
self.x = x
Value = type('Value', (object,), {'__init__': init})
Your class attribute would have to be a member of the dict passed as the third argument, which has to be fully defined before type is called.
not quite, but you can make a class method that return a new instance of your class in whatever way you want with the classmethod decorator
>>> class Value:
def __init__(self, x):
self.x=x
def __repr__(self):
return f"{type(self).__name__}({self.x})"
#classmethod
def half(cls):
return cls(0.5)
>>> Value(10)
Value(10)
>>> Value.half()
Value(0.5)
>>>
look like in py3.9 you can combine it with the property decorator to accomplish just that, see linked documentation above (but I don't have it at the moment)
Simply, you can't because the class hasn't yet existed. But you can use either metaclass or class decorator to achieve the same goal as the following shows:
#Metaclass
class Meta(type):
def __init__(cls, clsname, clsbases, clsdict):
cls.half = cls(0.5)
class Value(metaclass=Meta):
def __init__(self, x):
self.x = x
#Decorator
def decorator(cls):
cls.half = cls(0.5)
return cls
#decorator
class Value2:
def __init__(self, x):
self.x = x
print(Value.half.half.x)
print(Value.half.x)
print(Value2.half.half.x)
print(Value2.half.x)
I need to access the value of an attribute defined at the parent class inside an inner class, here's the code:
class main(object):
def __init__(self):
self.session_id = None
self.devices = self.Devices(self.session_id)
class Devices(object):
def __init__(self, session_id):
self.session_id = session_id
And here's how I would like to use it:
>>> m = main()
>>> m.session_id = 1
>>> m.session_id
1
>>> m.devices.session_id
>>>
My expectation is that m.devices.session_id will always have the exact same value as m.session_id. I understand that at this point when I instantiate the inner class the session_id value is passed down as None because that's how it was initiated but I'm not sure how I can keep both values the same without doing something very ugly like:
m.devices.session_id = m.session_id
outside the class code.
How can I accomplish that inside the class itself ?
The other answer works, but I think this is a better design: lose the nested class, and add a getter on the device object to lookup a backref:
class Main(object):
def __init__(self):
self.session_id = None
self.devices = Devices(main_obj=self)
class Devices(object):
def __init__(self, main_obj):
self.main_obj = main_obj
...
#property
def session_id(self):
return self.main_obj.session_id
The difference here is that you're not storing the same data twice, so they can not get out of sync - there is only one "source of truth" for the session_id (on main object).
In the earlier answer, the data is actually stored in two different namespaces and will get out of sync as easily as m.devices.session_id = 123.
You can do it like this:
class main(object):
def __init__(self):
self._session_id = None
self.devices = self.Devices(self._session_id)
#property
def session_id(self):
return self._session_id
#session_id.setter
def session_id(self, value):
self._session_id = self.devices.session_id = value
class Devices(object):
def __init__(self, session_id):
self.session_id = session_id
I have a Class B inheriting Class A with a class attribute cls_attr.
And I would like to set dynamically cls_attr in class B.
Something like that:
class A():
cls_attr= 'value'
class B(A):
def get_cls_val(self):
if xxx:
return cls_attr = 'this_value'
return cls_attr = 'that_value'
cls_attr = get_cls_val()
I tried several things. I know i might not be looking in the right place but i am out of solutions.
EDIT: Classes are django admin classes
Thanks.
class attributes can be read on the class or an instance, but you can only set them on the class (trying to set them on an instance will only create an instance attribute that will shadow the class attribute).
If the condition is known at import time, you can just test it in the class body:
xxx = True
class A(object):
cls_attr = 'value'
class B(A):
if xxx:
cls_attr = 'this_value'
else
cls_attr = 'that_value'
Now if you want to change it during the program's execution, you either have to use a classmethod:
class B(A):
#classmethod
def set_cls_attr(cls, xxx):
if xxx:
cls.cls_attr = 'this_value'
else:
cls.cls_attr = 'that_value'
or if you need to access your instance during the test:
class B(A):
def set_cls_attr(self, xxx):
cls = type(self)
if xxx:
cls.cls_attr = 'this_value'
else:
cls.cls_attr = 'that_value'
What about using classmethod and polymorphically overriding it in subclass?
class A:
#classmethod
def cls_attr(cls):
return 'value'
class B(A):
#classmethod
def cls_attr(cls):
if cond():
return 'this'
else:
return 'that'
assert A.cls_attr() == 'value'
cond = lambda: True
assert B.cls_attr() == 'this'
cond = lambda: False
assert B.cls_attr() == 'that'
The easiest solution for me is with property decorator:
class B:
#property
def attr_name(self):
""" do your stuff to define attr_name dynamically """
return attr_name
This seems to do what you want:
>>> class B(A):
#classmethod
def set_cls_val(cls, x):
if x == 1:
cls.cls_attr = "new"
>>> c = B()
>>> c.cls_attr
'value'
>>> c.set_cls_val(B, 1)
>>> c.cls_attr
'new'
>>> B.cls_attr
'new'
Just set it within the function.
EDIT: Updated to set the class attribute and not the instance attribute, thanks #bruno-desthuilliers.
EDIT: Updates once again, thanks #bruno-desthuilliers. I should think my answers through more clearly. But what you want is answered below.
I've got a class which contains a number of lists where whenever something is added to one of the lists, I need to trigger a change to the instance's state. I've created a simple demonstration class below to try to demonstrate what I'm trying to do.
Suppose I have a class like this:
class MyClass:
added = False
def _decorator(self, f):
def func(item):
added = true
return f(item)
return func
def __init__(self):
self.list = [1, 2, 3]
self.list.append = self._decorator(self.list.append)
Since a list is built in, I cannot change it's .append method
cls = MyClass() #gives me an AttributeError since '.append' is readonly
Ideally, I could do the following:
cls = MyClass()
cls.list.append(4)
cls.added #would be true
How should I go about this? Would subclassing list allow me to change it's behavior in this way? If so, how would I pass in the class's state without changing the methods signature?
Thanks!
You cannot monkey-patch builtins, so subclassing is the only way (and actually better and cleaner IMHO). I'd go for something like this:
class CustomList(list):
def __init__(self, parent_instance, *args, **kwargs):
super(CustomList, self).__init__(*args, **kwargs)
self.parent_instance = parent_instance
def append(self, item):
self.parent_instance.added = True
super(CustomList, self).append(item)
class MyClass(object):
added = False
def __init__(self):
self.list = CustomList(self, [1,2,3])
c = MyClass()
print c.added # False
c.list.append(4)
print c.added # True
Would this suit your needs?
class MyClass(object):
added = False
def __init__(self):
self.list = [1,2,3]
def append(self, obj):
self.added = True
self.list.append(obj)
cls = MyClass()
cls.append(4)
cls.added #true
It might be helpful to know what exactly you're trying to achieve.