I have the following class
class Foo():
data = "abc"
And i subclass it
class Bar(Foo):
data +="def"
I am trying to edit a parent class attribute in subclass. I want my parent class to have some string, and my subclass should add some extra data to that string. How it should be done in Python? Am i wrong by design?
You ask two questions:
How it should be done in Python?
class Bar(Foo):
data = Foo.data + "def"
Am i wrong by design?
I generally don't use class variables in Python. A more typical paradigm is to initialize an instance variable:
>>> class Foo(object):
... data = "abc"
...
>>> class Bar(Foo):
... def __init__(self):
... super(Bar, self).__init__()
... self.data += "def"
...
>>> b = Bar()
>>> b.data
'abcdef'
>>>
You can initialize the state of the Foo class from the Bar class and then append to the data like so:
class Foo(object):
def __init__(self):
self.data = "abc"
class Bar(Foo):
def __init__(self):
super(Bar, self).__init__()
self.data += "def"
When you instantiate an instance of Bar, the value of data that results is:
abcdef
You can refer to class variables of Foo from Bar's namespace via regular attribute access:
class Bar(Foo):
data = Foo.data + 'def'
I don't know if it's a good practice, but there's no reason for it to be bad.
Related
Consider the code snippet for the base class Base and its two child classes ChildA and ChildB:
class Base():
map = {}
def __init__(self, x):
type(self).map[x] = self
#classmethod
def get_state(cls, x):
if x in cls.map:
return cls.map[x]
return None
class ChildA(Base):
pass
class ChildB(Base):
pass
When creating an instance a = ChildA('foo') and calling ChildB.get_state('foo') after this, it returns the instance of ChildA.
However, expected (or rather the wishful behaviour) was None. The map attribute seems to be attached to the parent class Base, and the child classes share it.
Of course I could simply add a specific attribute to the child classes
class ChildA(Base):
map = {}
class ChildB(Base):
map = {}
but this is verbose if you have many child classes and several of those attributes. Is there any way to solve this by inheritance from Base?
You can use the init subclass hook:
class Base:
def __init_subclass__(cls, **kwargs):
cls.map = {}
Demo:
>>> class ChildA(Base):
... ...
...
>>> class ChildB(Base):
... ...
...
>>> ChildA.map == ChildB.map == {}
True
>>> ChildA.map is ChildB.map
False
Let's say I do this class:
class Person:
__slots__ = ["j"]
def __init__(self):
self.j = 1
def hello(self):
print("Hello")
Is the method hello in the slots?
Whether or not you're using __slots__ to control instance attributes, methods are stored on the class, not the instance:
>>> class Slots:
__slots__ = ['attr']
def __init__(self):
self.attr = None
def method(self):
pass
>>> class NoSlots:
def __init__(self):
self.attr = None
def method(self):
pass
>>> 'method' in Slots.__dict__
True
>>> 'method' in NoSlots.__dict__
True
>>> 'method' in NoSlots().__dict__
False
Using __slots__ actually makes all defined attributes descriptors (see also the how-to), which are also stored on the class:
>>> 'attr' in Slots.__dict__
True
>>> type(Slots.attr)
<class 'member_descriptor'>
I'm trying to access the methods of the class from which it was instantiated another class, I mean, accessing to the "parent" instance without creating a new instance of it.
class A():
def __init__(self):
...
b_instance = B()
...
class B():
def __init__(self):
...
def function1(self):
...
def function2(self):
C().run() # I need to use class C functionalities
...
class C():
def __init__(self):
...
def run(self):
classB.function1() #I can't access to these methods without instantiating again class B
# I have to execute:
>>> a = A()
>>> a.b_instance.function2()
Sorry if I have not explained well, is a bit confusing. If you need any clarification do not hesitate to ask.
EDIT.
In class C a specific handling of the execution of class B methods is done. Is not possible to instanciate again inside C because class B contains the initialization of hardware.
It's still not clear what exactly you're trying to achieve, but here's one fix:
class A():
def __init__(self):
...
b_instance = B()
...
class B():
def __init__(self):
...
def function1(self):
...
def function2(self):
C().run(self) # pass class B instance to C instance run method
...
class C():
def __init__(self):
...
def run(self, classB): # note additional parameter
classB.function1()
However, note that this represents a very high level of coupling between your various classes, which seems suspicious to me and may indicate a deeper flaw in your design.
This can access the class methods from other classes.
use instance method, class methods and static methods, if you are using various types of functins.
class A():
def __init__(self):
print 'in __init__'
self.b_instance = B() # making an instance of class
#self.b_instance.function2()
class B():
def __init__(self):
print 'in __init__, B'
#staticmethod
def function1():
print 'func1'
def function2(self):
C().run() # I need to use class C functionalities
# if you trying to access `run` method of `class C` make
# it instance bound method"""
class C():
def __init__(self):
pass
def run(self):
print 'in run'
B.function1() #I can't access to these methods without instantiating again class B
#you are passing class instance as `B` while calling function1
# so make it either classmethod `#classmethod` or `static method`
# I have to execute:
a = A()
a.b_instance.function2() # calling b_instance variable of class A
I am wondering if there is a way to programatically write an attribute to the class of a parent in which a function is run. I am looking for something like the following.
def add_items():
setattr(ParentClass, 'foo', 'bar')
class A(object):
add_items()
class B(object):
add_items()
a = A()
b = B()
print a.foo
"bar"
print b.foo
"bar"
It is not clear to me what you are after. Here are two attempts to answer your question.
You can make a decorator which adds attributes to a class:
def add_items(cls):
cls.foo = "bar"
return cls
#add_items
class A(object):
pass
On the other hand, if your function is a method, it can add whatever attributes it wants to the class instance:
class B(object):
def add_items(self):
self.foo = 'Baz'
b = B()
b.add_items()
Given an arbitrary object:
class Val(object):
def __init__(self):
this_val = 123
I want to create an abstract base class which has an attribute that is a Val():
class A(object):
foo = Val()
I would expect that when my children inherit from that class, they would get copies of Val(). For example:
class B(A):
pass
class C(A):
pass
I would expect the following behavior:
>>> b = B()
>>> c = C()
>>> c.foo.this_val = 456
>>> b.foo.this_val
123
But instead I get:
>>> b.this_val
456
I understand that I could just self.foo = Val() into the init to achieve that behavior, but I have a requirement that foo remain an attribute (it is a model manager in django). Can anyone suggest a work around for this?
EDIT: I really need to be able to access the value as a class attribute, so my desired behavior is:
>>> C.foo.this_val = 456
>>> B.foo.this_val
123
The attribute foo only exists on A. You will have to use a metaclass to add a new Val to each class.
class Val(object):
def __init__(self):
self.this_val = 123
class MC(type):
def __init__(self, name, bases, d):
super(MC, self).__init__(name, bases, d)
self.foo = Val()
class A(object):
__metaclass__ = MC
class B(A):
pass
B.foo.this_val = 456
print A.foo.this_val
print B.foo.this_val
Maybe using a descriptor would suit your requirements:
class Val(object):
def __init__(self):
self.this_val = 123
class ValDesc(object):
def __init__(self):
self.cls_lookup = {}
def __get__(self, obj, objtype=None):
return self.cls_lookup.setdefault(objtype, Val())
class A(object):
foo = ValDesc()
class B(A):
pass
class C(A):
pass
Now, as long as you make sure you don't set the instance attribute "foo" of any of your objects, they will have a class attribute that is individual to each subclass:
b = B()
c = C()
cc = C()
c.foo.this_val = 456
print c.foo.this_val # 456
print cc.foo.this_val # 456
print b.foo.this_val # 123
EDIT: With the edit I made some hours ago, changing the key in __get__ to be objtype instead of obj.__class__, this also works when accessing the class attributes directly:
print B.foo.this_val # 123
print C.foo.this_val # 456
Do both.
Make it a class attribute, but also initialize it to a fresh instance in the __init__ function. That way the reference stored isn't a shared one.