Python: Inheritance of a class attribute (list)
-
21-09-2019 - |
문제
inheriting a class attribute from a super class and later changing the value for the subclass works fine:
class Unit(object):
value = 10
class Archer(Unit):
pass
print Unit.value
print Archer.value
Archer.value = 5
print Unit.value
print Archer.value
leads to the output:
10
10
10
5
which is just fine: Archer inherits the value from Unit, but when I change Archer's value, Unit's value remains untouched.
Now, if the inherited value is a list, the shallow copy effect strikes and the value of the superclass is also affected:
class Unit(object):
listvalue = [10]
class Archer(Unit):
pass
print Unit.listvalue
print Archer.listvalue
Archer.listvalue[0] = 5
print Unit.listvalue
print Archer.listvalue
Output:
10
10
5
5
Is there a way to "deep copy" a list when inheriting it from the super class?
Many thanks
Sano
해결책
It is not a matter of shallow or deep copies, it is a matter of references and assignments.
It the first case Unit.value
and Archer.value
are two variables which reference the same value. When you do Archer.value = 5
, you are assigning a new reference to Acher.value.
To solve your problem you need to assign a new list value to the Archer.list
.
If these values are only going to be accessed by class methods, then the simplest solution is to do the assignment when the class is initialized.
다른 팁
Michael's answer is nice and simple, but if you wish to avoid having to add that line to each Unit subclass - maybe you have a bunch of other lists like that one, a metaclass is an easy way to solve the problem
class UnitMeta(type):
def __init__(self, *args):
super(UnitMeta, self).__init__(*args)
self.listvalue = [10]
class Unit(object):
__metaclass__ = UnitMeta
pass
class Archer(Unit):
pass
print Unit.listvalue
print Archer.listvalue
Archer.listvalue[0] = 5
print Unit.listvalue
print Archer.listvalue
output:
[10]
[10]
[10]
[5]
You can also extend this same idea to automatically find and copy up lists (and dicts) defined in Unit
class UnitMeta(type):
def __init__(self, *args):
super(UnitMeta, self).__init__(*args)
for superclass in self.__mro__:
for k,v in vars(superclass).items():
if isinstance(v, (list, dict, )):
setattr(self, k, type(v)(v))
class Unit(object):
__metaclass__ = UnitMeta
listvalue = [10]
class Archer(Unit):
pass
You coud copy the list in the definition of Archer:
class Archer(Unit):
listvalue = Unit.listvalue[:]