我试图使用python属性和继承,而且某些东西并不直观。我希望能够利用继承类的属性获取器和设置器,以避免重复代码重复行为。
为了解决我的问题,我创建了以下示例。我有一辆汽车,其行为可以计算乘客,并在给予一定数量的乘客(使用乘客属性吸气剂和安装者)时填充汽车座椅。对于有3排座位的面包车,我只需要定义第3个座位的行为,并按照前2行继承的Car类...
class Car(object):
def __init__(self):
#Store vehicle passengers in a dictionary by row
# - by default we'll have 1 in the front seat and 1 in the back
self._contents = {'row1': 1,
'row2': 1}
@property
def occupants(self):
"""
Number of occupants in the vehicle
"""
#Get the number of people in row 1
row1 = self._contents['row1']
#Get the number of people in row 2
row2 = self._contents['row2']
return row1 + row2
@occupants.setter
def occupants(self, val):
#Start with an empty car
self._contents['row1'] = 0
self._contents['row2'] = 0
#Check to see whether there are more than 2 people entering the car
if val > 2:
#Put 2 in the front seats
self._contents['row1'] = 2
#Put the rest in the back seat - no matter how many there are!
self._contents['row2'] = val - 2
else:
#Since there are 2 or fewer people, let them sit in the front
self._contents['row1'] = val
class Van(Car):
def __init__(self):
super(Van, self).__init__()
#Van's have an additional 3rd row
self._contents['row3'] = 1
@property
def occupants(self):
#Number of people in first 2 rows
first_2_rows = super(Van, self).occupants
#Number of people in 3rd row
row3 = self._contents['row3']
#Total number of people
return first_2_rows + row3
@occupants.setter
def occupants(self, val):
#Start with an empty van (Car class handles row1 and row2)
self._contents['row3'] = 0
#Check if there are more than 4 people entering the van
if val > 4:
#Put all but 4 folks in the back row
self._contents['row3'] = val - 4
#Load the first 2 rows in the same manner as for a car
#This causes an AttributeError
super(Van, self).occupants = 4
else:
#This causes an AttributeError
super(Van, self).occupants = val
if __name__ == '__main__':
van = Van()
print "Van has {0} people".format(van.occupants)
print "Seating 6 people in the van..."
van.occupants = 6
print "Van has {0} people".format(van.occupants)
我得到的输出如下:
Van has 3 people
Seating 6 people in the van...
Traceback (most recent call last):
File "C:/scratch.py", line 74, in <module>
van.occupants = 6
File "C:/scratch.py", line 65, in occupants
super(Van, self).occupants = 4
AttributeError: 'super' object has no attribute 'occupants'
Process finished with exit code 1
我特别感兴趣的是超类的getter工作正常,但是当我尝试使用setter时,我得到了属性错误。我错误地使用super()
了吗?有更好的方法吗?
我的实际应用涉及在文本文件格式和类字典数据结构之间进行读/写。文本文件中的一些内容由我的基类解析出来,其他一些特殊参数由子类处理。在子类setter中,我想首先让基类从文本文件中解析它所需的任何内容(填充数据结构),然后让子类解析在继承的数据结构中存储的其他值。
有些研究引导我this,最终Issue 505028将其宣布为&#34;功能&#34;而不是一个错误。那么,除此之外,有没有办法使用属性和继承使上述逻辑工作?我是否必须使用Car.occupants.fset(self, 4)
或其他内容?我可以在一分钟内回答自己,但我会继续发布并与人分享。如果它是重复的话,那就太好了。
编辑: 修复了一些额外的错误,例如在设置占用者之前清空所有座位,并且Van占用者设置器中的数字逻辑是错误的和不完整的(只有在修复了属性错误后才会变得明显)。
答案 0 :(得分:1)
正如您所说,Guido van Rossum says,
...超级语义......仅适用于 代码属性,而不是数据属性。
因此,解决方法是调用代码属性,而不是数据属性。这意味着,在这种情况下,您需要保持对 setter方法的引用;我们称之为set_occupants
。而不是
@occupants.setter
def occupants(self, val):
使用
def set_occupants(self, val):
...
occupants = property(get_occupants, set_occupants)
而不是super(...).occupants = 4
,你可以调用super的方法:
super(Van, self).set_occupants(4)
class Car(object):
def __init__(self):
#Store vehicle passengers in a dictionary by row
# - by default we'll have 1 in the front seat and 1 in the back
self._contents = {'row1': 1,
'row2': 1}
def get_occupants(self):
"""
Number of occupants in the vehicle
"""
#Get the number of people in row 1
row1 = self._contents['row1']
#Get the number of people in row 2
row2 = self._contents['row2']
return row1 + row2
def set_occupants(self, val):
#Check to see whether there are more than 2 people entering the car
if val > 2:
#Put 2 in the front seats
self._contents['row1'] = 2
#Put the rest in the back seat - no matter how many there are!
self._contents['row2'] = val - 2
else:
#Since there are 2 or fewer people, let them sit in the front
self._contents['row1'] = val
occupants = property(get_occupants, set_occupants)
class Van(Car):
def __init__(self):
super(Van, self).__init__()
#Van's have an additional 3rd row
self._contents['row3'] = 1
def get_occupants(self):
#Number of people in first 2 rows
first_2_rows = super(Van, self).occupants
#Number of people in 3rd row
row3 = self._contents['row3']
#Total number of people
return first_2_rows + row3
def set_occupants(self, val):
#Check if there are more than 4 people entering the van
if val > 4:
#Put all but 4 folks in the back row
self._contents['row3'] = val - 4
#Load the first 2 rows in the same manner as for a car
super(Van, self).set_occupants(4)
occupants = property(get_occupants, set_occupants)
if __name__ == '__main__':
van = Van()
print "Van has {0} people".format(van.occupants)
print "Seating 6 people in the van..."
van.occupants = 6
print "Van has {0} people".format(van.occupants)
产量
Van has 3 people
Seating 6 people in the van...
Van has 6 people
要继续使用@property装饰器,但仍然可以从super
,和调用setter,而无需手动添加大量额外属性,您可以使用元类为你做的工作。类装饰器也是可能的,但是元类的优点是你只需要将它定义为Car
的元类,然后元类及其行为由Car
的所有子类继承。而,必须手动将类装饰器应用于每个子类。
class MetaCar(type):
def __init__(cls, name, bases, clsdict):
super(MetaCar, cls).__init__(name, bases, clsdict)
for name, val in clsdict.items():
if isinstance(val, property):
setattr(cls, 'get_{}'.format(name), val.fget)
setattr(cls, 'set_{}'.format(name), val.fset)
setattr(cls, 'del_{}'.format(name), val.fdel)
class Car(object):
__metaclass__ = MetaCar
def __init__(self):
#Store vehicle passengers in a dictionary by row
# - by default we'll have 1 in the front seat and 1 in the back
self._contents = {'row1': 1,
'row2': 1}
@property
def occupants(self):
"""
Number of occupants in the vehicle
"""
#Get the number of people in row 1
row1 = self._contents['row1']
#Get the number of people in row 2
row2 = self._contents['row2']
return row1 + row2
@occupants.setter
def occupants(self, val):
#Check to see whether there are more than 2 people entering the car
if val > 2:
#Put 2 in the front seats
self._contents['row1'] = 2
#Put the rest in the back seat - no matter how many there are!
self._contents['row2'] = val - 2
else:
#Since there are 2 or fewer people, let them sit in the front
self._contents['row1'] = val
class Van(Car):
def __init__(self):
super(Van, self).__init__()
#Van's have an additional 3rd row
self._contents['row3'] = 1
@property
def occupants(self):
#Number of people in first 2 rows
first_2_rows = super(Van, self).occupants
#Number of people in 3rd row
row3 = self._contents['row3']
#Total number of people
return first_2_rows + row3
@occupants.setter
def occupants(self, val):
#Check if there are more than 4 people entering the van
if val > 4:
#Put all but 4 folks in the back row
self._contents['row3'] = val - 4
#Load the first 2 rows in the same manner as for a car
super(Van, self).set_occupants(4)
if __name__ == '__main__':
van = Van()
print "Van has {0} people".format(van.occupants)
print "Seating 6 people in the van..."
van.occupants = 6
print "Van has {0} people".format(van.occupants)
答案 1 :(得分:0)
环顾网络,我发现SomeClass.property.fset(self, value)
可以用来调用属性的setter。在这种情况下,SomeClass是Car(Van的超级类),self是当前的Van实例,汽车占用者设置器操作以填充面包车的前两行(就像汽车一样)。
class Car(object):
def __init__(self):
#Store vehicle passengers in a dictionary by row
# - by default we'll have 1 in the front seat and 1 in the back
self._contents = {'row1': 1,
'row2': 1}
@property
def occupants(self):
"""
Number of occupants in the vehicle
"""
#Get the number of people in row 1
row1 = self._contents['row1']
#Get the number of people in row 2
row2 = self._contents['row2']
return row1 + row2
@occupants.setter
def occupants(self, val):
#Start with an empty car
self._contents['row1'] = 0
self._contents['row2'] = 0
#Check to see whether there are more than 2 people entering the car
if val > 2:
#Put 2 in the front seats
self._contents['row1'] = 2
#Put the rest in the back seat - no matter how many there are!
self._contents['row2'] = val - 2
else:
#Since there are 2 or fewer people, let them sit in the front
self._contents['row1'] = val
class Van(Car):
def __init__(self):
super(Van, self).__init__()
#Van's have an additional 3rd row
self._contents['row3'] = 1
@property
def occupants(self):
#Number of people in first 2 rows
first_2_rows = super(Van, self).occupants
#Number of people in 3rd row
row3 = self._contents['row3']
#Total number of people
return first_2_rows + row3
@occupants.setter
def occupants(self, val):
#Start with an empty van (first 2 rows handled by Car class)
self._contents['row3'] = 0
#Check if there are more than 4 people entering the van
if val > 4:
#Put all but 4 folks in the back row
self._contents['row3'] = val - 4
#Load the first 2 rows in the same manner as for a car
Car.occupants.fset(self, 4)
else:
#Load the first 2 rows in the same manner as for a car
Car.occupants.fset(self, val)
if __name__ == '__main__':
van1 = Van()
van2 = Van()
print "Van has {0} people".format(van1.occupants)
print "Seating 6 people in van1"
van1.occupants = 6
print "Seating 2 people in van2"
van2.occupants = 2
print "van1 has {0} people".format(van1.occupants)
print "van2 has {0} people".format(van2.occupants)
输出结果为:
Van has 3 people
Seating 6 people in van1
Seating 2 people in van2
van1 has 6 people
van2 has 2 people
道具也向unutbu进行了大量工作,并证明这也可以通过属性函数或元类来解决。我还不确定哪种方法更优雅,因为每种方法都有亲和的方法。
如果在这种情况下回答我自己的问题是不好的形式,请打电话给我,我很乐意做任何必要的事情来遵循社区协议。