8.11 简化数�结构的�始化¶
问题¶
ä½ å†™äº†å¾ˆå¤šä»…ä»…ç”¨ä½œæ•°æ�®ç»“构的类,ä¸�想写太多烦人的 __init__() 函数
解决方案¶
å�¯ä»¥åœ¨ä¸€ä¸ªåŸºç±»ä¸å†™ä¸€ä¸ªå…¬ç”¨çš„ __init__() 函数:
import math
class Structure1:
# Class variable that specifies expected fields
_fields = []
def __init__(self, *args):
if len(args) != len(self._fields):
raise TypeError('Expected {} arguments'.format(len(self._fields)))
# Set the arguments
for name, value in zip(self._fields, args):
setattr(self, name, value)
ç„¶å�Žä½¿ä½ 的类继承自这个基类:
# Example class definitions
class Stock(Structure1):
_fields = ['name', 'shares', 'price']
class Point(Structure1):
_fields = ['x', 'y']
class Circle(Structure1):
_fields = ['radius']
def area(self):
return math.pi * self.radius ** 2
使用这些类的示例:
>>> s = Stock('ACME', 50, 91.1)
>>> p = Point(2, 3)
>>> c = Circle(4.5)
>>> s2 = Stock('ACME', 50)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "structure.py", line 6, in __init__
raise TypeError('Expected {} arguments'.format(len(self._fields)))
TypeError: Expected 3 arguments
如果还想支æŒ�关键å—å�‚数,å�¯ä»¥å°†å…³é”®å—å�‚数设置为实例属性:
class Structure2:
_fields = []
def __init__(self, *args, **kwargs):
if len(args) > len(self._fields):
raise TypeError('Expected {} arguments'.format(len(self._fields)))
# Set all of the positional arguments
for name, value in zip(self._fields, args):
setattr(self, name, value)
# Set the remaining keyword arguments
for name in self._fields[len(args):]:
setattr(self, name, kwargs.pop(name))
# Check for any remaining unknown arguments
if kwargs:
raise TypeError('Invalid argument(s): {}'.format(','.join(kwargs)))
# Example use
if __name__ == '__main__':
class Stock(Structure2):
_fields = ['name', 'shares', 'price']
s1 = Stock('ACME', 50, 91.1)
s2 = Stock('ACME', 50, price=91.1)
s3 = Stock('ACME', shares=50, price=91.1)
# s3 = Stock('ACME', shares=50, price=91.1, aa=1)
ä½ è¿˜èƒ½å°†ä¸�在 _fields ä¸çš„å��ç§°åŠ å…¥åˆ°å±žæ€§ä¸åŽ»ï¼š
class Structure3:
# Class variable that specifies expected fields
_fields = []
def __init__(self, *args, **kwargs):
if len(args) != len(self._fields):
raise TypeError('Expected {} arguments'.format(len(self._fields)))
# Set the arguments
for name, value in zip(self._fields, args):
setattr(self, name, value)
# Set the additional arguments (if any)
extra_args = kwargs.keys() - self._fields
for name in extra_args:
setattr(self, name, kwargs.pop(name))
if kwargs:
raise TypeError('Duplicate values for {}'.format(','.join(kwargs)))
# Example use
if __name__ == '__main__':
class Stock(Structure3):
_fields = ['name', 'shares', 'price']
s1 = Stock('ACME', 50, 91.1)
s2 = Stock('ACME', 50, 91.1, date='8/2/2012')
讨论¶
å½“ä½ éœ€è¦�使用大é‡�很å°�的数æ�®ç»“构类的时候,
相比手工一个个定义 __init__() 方法而已,使用这ç§�æ–¹å¼�å�¯ä»¥å¤§å¤§ç®€åŒ–代ç �。
在上é�¢çš„å®žçŽ°ä¸æˆ‘们使用了 setattr() 函数类设置属性值,
ä½ å�¯èƒ½ä¸�想用这ç§�æ–¹å¼�,而是想直接更新实例å—典,就åƒ�下é�¢è¿™æ ·ï¼š
class Structure:
# Class variable that specifies expected fields
_fields= []
def __init__(self, *args):
if len(args) != len(self._fields):
raise TypeError('Expected {} arguments'.format(len(self._fields)))
# Set the arguments (alternate)
self.__dict__.update(zip(self._fields,args))
尽管这也å�¯ä»¥æ£å¸¸å·¥ä½œï¼Œä½†æ˜¯å½“定义å�类的时候问题就æ�¥äº†ã€‚
当一个å�类定义了 __slots__ 或者通过property(或æ��述器)æ�¥åŒ…装æŸ�个属性,
那么直接访问实例å—典就ä¸�起作用了。我们上é�¢ä½¿ç”¨ setattr() ä¼šæ˜¾å¾—æ›´é€šç”¨äº›ï¼Œå› ä¸ºå®ƒä¹Ÿé€‚ç”¨äºŽå�类情况。
这�方法唯一�好的地方就是对�些IDE而言,在显示帮助函数时�能�太�好。比如:
>>> help(Stock)
Help on class Stock in module __main__:
class Stock(Structure)
...
| Methods inherited from Structure:
|
| __init__(self, *args, **kwargs)
|
...
>>>
å�¯ä»¥å�‚考9.16å°�节æ�¥å¼ºåˆ¶åœ¨ __init__() æ–¹æ³•ä¸æŒ‡å®šå�‚数的类型ç¾å��。