python: set read-only attribute for class objects - python

I've created a class object called 'Node'. I then created two subclasses 'Beetle' and 'Dakota'. You'll notice there is an attr called 'superclass' which is part of the baseclass. I want this attribute to be set for each subclass and once it's set it should never be changed. It's a read-only attribute. I was wondering how to set this attribute up correctly in order to be a read only attribute?
class Node(object):
def __init__(self, name, superclass, attributes, children):
self.name = name
self.superclass = superclass
self.attributes = attributes if attributes is not None else {}
self.children = children if children is not None else []
class Beetle(Node):
def __init__(self, name="", superclass="Cars", attributes=None, children=None, enabled=True):
super(Beetle, self).__init__(name=name, superclass=superclass, attributes=attributes, children=children)
self.enabled = enabled
class Dakota(Node):
def __init__(self, name="", superclass="Trucks", attributes=None, children=None, enabled=True):
super(Dakota, self).__init__(name=name, superclass=superclass, attributes=attributes, children=children)
self.enabled = enabled

Rename you variable to add __ to the begining
self.__superclass = superclass
you can't access self.__superclass with something like Dakota().__superclass
if you want to get value of __superclass add a function in Node class to return it.
def getsuperclass(self):
return self.__superclass
...
Dakota().getsuperclass()

To have a 'read-only' property in a class you can make use of the #property decoration.
An example:
class Dakota(object):
def __init__(self, superclass):
self.__superclass = superclass
#property
def superclass(self):
return self.__superclass
Usage:
>>> a = Dakota('lol')
>>> a.superclass
'lol'
>>> a.superclass = 'hah'
AttributeError...
AttributeError: can't set attribute
You can refer to this other answer thread

Related

Create child class object using parent class instance

lets say we have class A and it has one instance - x. How to make a child class of class A where I would be able to pass x as an argument and get all its parameters and pass it to child class object. precisely speaking I want to do something like this.
class A:
def __init__(self, parameter1, parameter2):
self.parameter1 = parameter1
self.parameter2 = parameter2
class B(A):
def __init__(self, Ainstance, someParameter):
super().__init__(**Ainstance.__dict__)
self.someParameter = someParameter
x = A(parameter1='1', parameter2='2')
x = B(x, someParameter='3')
print(x.parameter1)
print(x.parameter2)
print(x.someParameter)
the goal is to create a class where I would be able to get all the parameters of parent class object, and add my own attributes. The problem in the code above is I won't be able to do that with all classes because not all of them has __dict__ attribute.
I have this example code which I use to remind myself how to construct a proxy.
#soProxyPattern
class Example:
def __init__(self):
self.tag_name = 'name'
def foo(self):
return 'foo'
def bar(self, param):
return param
class Container:
def __init__(self, contained):
self.contained = contained
self.user_name = 'username'
def zoo(self):
return 0
def __getattr__(self, item):
if hasattr(self.contained, item):
return getattr(self.contained,item)
#raise item
c = Container(Example())
print(c.zoo())
print(c.foo())
print(c.bar('BAR'))
print(c.tag_name)
print(c.user_name)
The output is:
0
foo
BAR
name
username
This shows that Container can have its own attributes (methods or variables) which you can access over and above all of the attributes of the contained instance.
Instead of dict you could use the dir and getattr like this:
class A:
def __init__(self, parameter1, parameter2):
self.parameter1 = parameter1
self.parameter2 = parameter2
class B(A):
def __init__(self, Ainstance, someParameter):
parameters = {param: getattr(Ainstance, param) for param in dir(Ainstance) if not param.startswith("__")}
super().__init__(**parameters)
self.someParameter = someParameter
For a more detailed explanation see: Get all object attributes in Python?

Inner class overwriting class property

I have a Python script containing the following code:
class Parent:
def __init__(self, name):
self.name = name
class Child:
def __init__(self, name):
self.name = name
def print_relationship(self):
print(f'{self.name} is {self.Child.name}\'s parent')
myObj_1 = Parent('John')
myObj_1.Child.name = 'Steve'
myObj_2 = Parent('Stan')
myObj_2.Child.name = 'Oliver'
myObj_1.print_relationship()
myObj_2.print_relationship()
which returns:
>>> John is Oliver's parent
>>> Stan is Oliver's parent
but, I'm expecting the following results:
>>> John is Steve's parent
>>> Stan is Oliver's parent
Is this an inheritance issue? Bad design? Both?
-edit- Bad design was the answer.
The class Child represents children in general. Define it globally. Each instance of Parent should store a reference to an instance of Child. (That instance may not be unique; a child could have multiple parents.)
class Child:
def __init__(self, name):
self.name = name
class Parent:
def __init__(self, name, child=None):
self.name = name
self.child = child
def print_relationship(self):
print(f'{self.name} is {self.child.name}\'s parent')
oliver = Child('Oliver')
myObj_1 = Parent('John', Child('Steve'))
myObj_2 = Parent('Stan', oliver)
myObj_3 = Parent('Jane', oliver)
myObj_1.print_relationship()
myObj_2.print_relationship()
Of course, a parent could have multiple children:
class Parent:
def __init__(self, name, children=None):
self.name = name
if children is None:
children = []
self.children = children
def print_relationship(self):
for child in self.children:
print(f'{self.name} is {child.name}\'s parent')
myObj1 = Parent('Bob', [Child('Alice'), Child('Chloe')])
In the linemyObj_1.Child.name = 'Steve', you are creating a class attribute called name on Child class and assigning value of Steve. This name is different from the instance attribute in the line self.name = name which can only be accessed by instance of the class Child.
When myObj_2.Child.name = 'Oliver' gets executed, the value of class attribute name of Child class is changed.
In print_relationship, you are referring to self.Child.name, which refers to the class attribute name of Child class.

Listing instance attributes with __dict__

Hi I am trying to use composition to create a new class using instances of another class when I try to turn the new object into a dictionary using __dict__, it's shows me <__main__.myobjec object at 0x00000000029CA908>, not sure am I using the __dict__ incorrectly though I have heard its related to new classes, any help greatly appreciated.
class User:
def __init__(self, name, job=None):
self.name = name
self.job = job
class Customer(User):
_ID = 100
def __init__(self, name, job=None):
self.name = name
self.job = job
class Account:
_ID = 0
def __init__(self, name, job=None):
self.customer = Customer(name , "Customer")
def __getattr__(self, attr):
return getattr(self.customer, attr)
>>> A = Account("Abdi")
>>> A.__dict__
{'customer': <__main__.Customer object at 0x109fdbfc8>}
>>>
You need to implement the __repr__ method to represent all of the instances of the Customer class.
def __repr__(self): return repr(self.__dict__) # the dictionary of attributes in __repr__

Python Class Name as Class Variable

I'm working as an application with classes and subclasses. For each class, both super and sub, there is a class variable called label. I would like the label variable for the super class to default to the class name. For example:
class Super():
label = 'Super'
class Sub(Super):
label = 'Sub'
Rather than manually type out the variable for each class, is it possible to derive the variable from the class name in the super class and have it automatically populated for the subclasses?
class Super():
label = # Code to get class name
class Sub(Super)
pass
# When inherited Sub.label == 'Sub'.
The reason for this is that this will be the default behavior. I'm also hoping that if I can get the default behavior, I can override it later by specifying an alternate label.
class SecondSub(Super):
label = 'Pie' # Override the default of SecondSub.label == 'SecondSub'
I've tried using __name__, but that's not working and just gives me '__main__'.
I would like to use the class variable label in #classmethod methods. So I would like to be able to reference the value without having to actually create a Super() or Sub() object, like below:
class Super():
label = # Magic
#classmethod
def do_something_with_label(cls):
print(cls.label)
you can return self.__class__.__name__ in label as a property
class Super:
#property
def label(self):
return self.__class__.__name__
class Sub(Super):
pass
print Sub().label
alternatively you could set it in the __init__ method
def __init__(self):
self.label = self.__class__.__name__
this will obviously only work on instantiated classes
to access the class name inside of a class method you would need to just call __name__ on the cls
class XYZ:
#classmethod
def my_label(cls):
return cls.__name__
print XYZ.my_label()
this solution might work too (snagged from https://stackoverflow.com/a/13624858/541038)
class classproperty(object):
def __init__(self, fget):
self.fget = fget
def __get__(self, owner_self, owner_cls):
return self.fget(owner_cls)
class Super(object):
#classproperty
def label(cls):
return cls.__name__
class Sub(Super):
pass
print Sub.label #works on class
print Sub().label #also works on an instance
class Sub2(Sub):
#classmethod
def some_classmethod(cls):
print cls.label
Sub2.some_classmethod()
You can use a descriptor:
class ClassNameDescriptor(object):
def __get__(self, obj, type_):
return type_.__name__
class Super(object):
label = ClassNameDescriptor()
class Sub(Super):
pass
class SecondSub(Super):
label = 'Foo'
Demo:
>>> Super.label
'Super'
>>> Sub.label
'Sub'
>>> SecondSub.label
'Foo'
>>> Sub().label
'Sub'
>>> SecondSub().label
'Foo'
If class ThirdSub(SecondSub) should have ThirdSub.label == 'ThirdSub' instead of ThirdSub.label == 'Foo', you can do that with a bit more work. Assigning label at the class level will be inherited, unless you use a metaclass (which is a lot more hassle than it's worth for this), but we can have the label descriptor look for a _label attribute instead:
class ClassNameDescriptor(object):
def __get__(self, obj, type_):
try:
return type_.__dict__['_label']
except KeyError:
return type_.__name__
Demo:
>>> class SecondSub(Super):
... _label = 'Foo'
...
>>> class ThirdSub(SecondSub):
... pass
...
>>> SecondSub.label
'Foo'
>>> ThirdSub.label
'ThirdSub'
A metaclass might be useful here.
class Labeller(type):
def __new__(meta, name, bases, dct):
dct.setdefault('label', name)
return super(Labeller, meta).__new__(meta, name, bases, dct)
# Python 2
# class Super(object):
# __metaclass__ = Labeller
class Super(metaclass=Labeller):
pass
class Sub(Super):
pass
class SecondSub(Super):
label = 'Pie'
class ThirdSub(SecondSub):
pass
Disclaimer: when providing a custom metaclass for your class, you need to make sure it is compatible with whatever metaclass(es) are used by any class in its ancestry. Generally, this means making sure your metaclass inherits from all the other metaclasses, but it can be nontrivial to do so. In practice, metaclasses aren't so commonly used, so it's usually just a matter of subclassing type, but it's something to be aware of.
As of Python 3.6, the cleanest way to achieve this is with __init_subclass__ hook introduced in PEP 487. It is much simpler (and easier to manage with respect to inheritance) than using a metaclass.
class Base:
#classmethod
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
if 'label' not in cls.__dict__: # Check if label has been set in the class itself, i.e. not inherited from any of its superclasses
cls.label = cls.__name__ # If not, default to class's __name__
class Sub1(Base):
pass
class Sub2(Base):
label = 'Custom'
class SubSub(Sub2):
pass
print(Sub1.label) # Sub1
print(Sub2.label) # Custom
print(SubSub.label) # SubSub

Access Child class variables in Parent class

I have a Parent class and a inherited child class, I would like to know how to access the child class variable in my Parent class..
I tried this and it fails -
class Parent(object):
def __init__(self):
print x
class Child(Parent):
x = 1;
x = Child();
Error:-
NameError: global name 'x' is not defined
This question is in relation to Django forms where we inherit the form class
and declare some class variables.
For example:-
My form looks like this
from django import forms
class EmployeeForm(forms.Form):
fname = forms.CharField(max_length=100)
lname = forms.CharField(max_length=100)
I believe the form fields are considered as class variable and somehow passed to the parent class..
Django does this with metaclasses. (Relevant Django source)
Here's a distilled example of the relevant code:
class Field(object):
def __init__(self, *args):
self.args = args
def __repr__(self):
return "Form(%s)" % (', '.join(map(repr, self.args)),)
class Meta(type):
def __new__(mcs, name, bases, attrs):
field_list = []
for k,v in attrs.items():
if isinstance(v, Field):
field_list.append(v)
cls = type.__new__(mcs, name, bases, attrs)
cls.fields = field_list
return cls
class Form(object):
__metaclass__ = Meta
class MyForm(Form):
fe1 = Field("Field1", "Vars1")
fe2 = Field("Field2", "Vars2")
x = "This won't appear"
form_fields = MyForm.fields
print(form_fields)
There are many questions on here about Python metaclasses (example), so I won't try to re-explain the concept.
In this case, when you create the class MyForm, each of the class attributes are checked for being instances of Field. If they are, they're added to a list (field_list).
The class is created, then an attribute .fields is added to the class, which is field_list, the list of Field elements.
You can then access the form fields through <FormSubclass>.fields or in the case of this example, MyForm.fields.
Edit:
It's worth noting that you can accomplish very similar functionality, without the metaclass syntactic sugar with something like:
class Field(object):
def __init__(self, *args):
self.args = args
def __repr__(self):
return "Form(%s)" % (', '.join(map(repr, self.args)),)
class Form(object):
def __init__(self):
self._fields = None
def fields(self):
if self._fields is None:
field_list = []
for k in dir(self):
v = getattr(self, k)
if isinstance(v, Field):
field_list.append(v)
self._fields = field_list
return self._fields
class MyForm(Form):
def __init__(self):
Form.__init__(self)
self.fe1 = Field("Field1", "Vars1")
self.fe2 = Field("Field2", "Vars2")
self.x = "This won't appear"
form_fields = MyForm().fields()
print(form_fields) # [Form('Field1', 'Vars1'), Form('Field2', 'Vars2')]
Short answer : you dont access subclasse's attributes from a parent class - because the parent class can not know what attributes a child class might have.
Long answer : ... unless the parent class defines a protocol allowing subclasses to let the parent class knows about at least part of it's own attributes.
Django's form framework (as well as django's orm FWIW) use such a protocol: the base Form class has a custom metaclass that collects the form.fields declared in a subclass - and do quite some black magic. FWIW, Django is oss so you could have answered the question yourself just reading the source code: https://github.com/django/django/blob/master/django/forms/forms.py
You need to refer to self.x to access Child class variables:
class Parent(object):
def __init__(self):
print(self.x)
class Child(Parent):
x = 1
if __name__ == '__main__':
child_instance = Child()
This might not help you in regards to Django Forms, but another alternative is to work with abstract classes. You would exchange attributes with methods/properties. It also prevents you from using the parent class by itself.
from abc import ABC, abstractmethod
class Parent(ABC):
#property
#abstractmethod
def x(self):
pass
def __init__(self):
print(self.x)
class Child(Parent):
#property
def x(self):
return 1
if __name__ == '__main__':
child_instance = Child() # prints "1"
parent_instance = Parent() # fails
Well, if I got you right... Maybe you're thinking of getting a field from the child class to work on the parent class. Well, that's polymorphism and it's done by overriding the parent class.
Let's assume you have :
A parent has x, now to increase x from the child and make it reflect in the parent, check the code below to get it.
class Parent:
def __init__(self, x):
self.x = x
def Print(self):
print(f"{self.x}")
class Child(Parent):
def __init__(self, x):
Parent.__init__(self, x)
x += 1
self.x = x
""""""
c1 = Child(2)
c1.Print()
#output: 3
""""""
c2 = Child(8)
c2.Print()
#output: 9

Categories

Resources