可以有一个“神奇的关键字”(显然只有在未指定**kwargs
时才有效),以便__init__(*args, ***pass_through_kwargs)
使所有意外的kwargs
直接传递给{ {1}}? (在后台,没有用户知识)这将使可读性更容易,并且可以通过自动完成和自动文档安全地忽略它。
在下图中,super().__init__
仅使用Mouse
进行实例化。只是为了让name
- 调用一行,我必须添加super
(以指定奶酪的**kwargs
)并手动将它们交给我。我不能告诉number_of_holes
和Mouse
“无论你不知道什么:只需将它直接传递给Animal
”?
当然你可能想要考虑碰撞,但我认为这可能会大大提高代码的可读性
答案 0 :(得分:3)
事实上, 是可能的。在提供演示代码之前,我先简单介绍一下:
这种自动属性访问非常糟糕的风格。你无法记录它,将让人们滥用/忽略封装概念。
根本不是不言自明的;你不能使用duck typing,因为突然间每个对象都可以有任意属性。
您真正想要做的是在具有清晰焦点并带来相应功能的对象中创建一致且易于理解的设计。
但是,如果您仍想测试它,可以使用**kwargs
并将向上传递给超级构造函数;它始终包含在方法签名中明确设置的 not 的所有关键字参数:
def testme(color='red', **kwargs):
print(kwargs)
testme(width=3)
# See that 'color' is not in kwargs dict if running this code:
>>> {'width': 3}
就是这种情况,因为显式设置的关键字参数被赋值给函数签名中给出的变量名;其余部分在kwargs
中捕获。
此行为允许您实际过滤每个期望的__init__
中的关键字参数,并将其余参数传递给超级类' __init__
:
class Animal(object):
def __init__(self, num_legs, **kwargs):
self.num_legs = num_legs
for key, value in kwargs.iteritems():
if not hasattr(self, key):
print(
'Generic attribute assignment: {:s} = {:s}'
.format(key, str(value)))
setattr(self, key, value)
def __str__(self):
res = '<Animal ({:s}):\n'.format(self.__class__.__name__)
for key in dir(self):
if key.startswith('__'):
continue
res += ' {:s} = {:s}\n'.format(key, str(getattr(self, key)))
res += '>'
return res
class Mouse(Animal):
def __init__(self, color='gray', **kwargs):
super(Mouse, self).__init__(4, **kwargs)
self.color = color
class MickeyMouse(Mouse):
def __init__(self, **kwargs):
super(MickeyMouse, self).__init__(color='black', **kwargs)
if __name__ == '__main__':
mm = MickeyMouse(dog='Pluto')
print(mm)
这将产生:
Generic attribute assignment: dog = Pluto
<Animal (MickeyMouse):
color = black
dog = Pluto
num_legs = 4
>
理由是,关键字'dog'
永远不会在任何__init__
方法中出现,因此始终保留在kwargs
中,直到最后一个超类&#39; __init__
遍历dict并设置相应的属性。打印分配消息的位置。之后,专门的__init__
方法会设置其值(此处为:'color'
)并且您已完成。
如果您的班级MickeyMouse
需要调用Cheese
和Mouse
的初始化,则需要先确定其依赖关系。
从类定义构造具体对象会产生该类的有状态表示。因此,如果您创建一个MickeyMouse
对象,则可能需要它调用其超类&#39;构造函数,例如,Mouse
类之一 - 给定MickeyMouse
继承自Mouse
。如果您现在希望初始化MickeyMouse
也调用Cheese
init,它将创建一个新对象,主要有两个对象:鼠标(即MickeyMouse
)和奶酪,没人知道它来自哪里。
但也许我还没有得到正确的应用案例。无论如何,关于你的实施方法:
正如您所注意到的,类的__init__
方法是无法重载的单个实例(魔术)方法。而且由于设计不可能,你不应该尝试重载,甚至不要创建试图实现类方法重载的方法交换实现。
在您的示例中,您声明您希望能够使用不同的args和kwargs组合创建单个类的实例,并且神奇地解析为正确的处理。
(1)仅使用kwargs:
class Cheese(object):
def __init__(self, num_holes=0, weight=None, name=None):
# Add logic depending on kwargs that are not None
pass
像这样使用它:
cheddar = Cheese()
gouda = Cheese(num_holes=3)
owned_cheddar = Cheese(name='MickeyMouse')
(2)使用静态构造函数方法。
class Cheese(object):
def __init__(self, num_holes, weight, name):
# Default construct using all args
pass
@staticmethod
with_owner(name):
return Cheese(0, None, name)
像这样使用它:
cheddar = Cheese(0, None, None)
owned_cheddar = Cheese.with_owner('MickeyMouse')
请注意,使用静态方法对于您的应用程序来说是不灵活的;在构建来自不同数据结构的对象时,例如.from_dict(...)
,.from_csv(...)
等,可以更好地使用它。