将参数指定给属性的Python方式?
示例代码如下所示:将参数指定给属性的Python方式?,python,oop,class,methods,Python,Oop,Class,Methods,示例代码如下所示: def assign(self, input=None, output=None, param=None, p1=None, p2=None): if input: self.input = input if output: self.output = output if param: self.param = param if p1: self.p1 = p1 if p2
def assign(self, input=None, output=None, param=None, p1=None, p2=None):
if input:
self.input = input
if output:
self.output = output
if param:
self.param = param
if p1:
self.p1 = p1
if p2:
self.p2 = p2
class Foo(object):
def assign(self, input=None, output=None, param=None, p1=None, p2=None):
for name in 'input output param p1 p2'.split():
val = vars()[name]
if val is not None:
setattr(self, name, val)
foo = Foo()
foo.assign(p1=123, p2='abc')
>>> def assign(self, input=None, output=None, param=None, p1=None, p2=None):
... pass
...
虽然这看起来很清楚,但是如果这个函数有10个参数,它就会受到影响。有没有人想到一种更方便的方法呢?你可以这样做:
def assign(self,**kwargs):
for k,v in kwargs.items():
if v:
setattr(self,k,v)
这非常简单,适用于许多情况。如果您想维护一组关键字,您将接受这些关键字并在其余部分引发TypeError:
#python2.7 and newer
def assign(self,allowed_kwargs={'foo','bar','baz'},**kwargs):
if kwargs.keysview() - allowed_kwargs:
raise TypeError('useful message here...')
for k in allowed_kwargs:
setattr(self,k,kwargs[k])
这在某种程度上也是可以检查的,因为用户将看到允许的KWARG集。显式优于隐式
相比之下,它有许多优点
def assign(self, **kwargs)
- 这是自我记录
- 如果将无效参数传递给,则会引发有用的
TypeError
分配
可以使用位置参数和关键字参数调用assign
if
-语句是单调的。为了减少单调,
您可以使用以下内容:
def assign(self, input=None, output=None, param=None, p1=None, p2=None):
if input:
self.input = input
if output:
self.output = output
if param:
self.param = param
if p1:
self.p1 = p1
if p2:
self.p2 = p2
class Foo(object):
def assign(self, input=None, output=None, param=None, p1=None, p2=None):
for name in 'input output param p1 p2'.split():
val = vars()[name]
if val is not None:
setattr(self, name, val)
foo = Foo()
foo.assign(p1=123, p2='abc')
>>> def assign(self, input=None, output=None, param=None, p1=None, p2=None):
... pass
...
python的一大优点是它的交互式解释器。当您编写这样的代码时:
def assign(self, input=None, output=None, param=None, p1=None, p2=None):
if input:
self.input = input
if output:
self.output = output
if param:
self.param = param
if p1:
self.p1 = p1
if p2:
self.p2 = p2
class Foo(object):
def assign(self, input=None, output=None, param=None, p1=None, p2=None):
for name in 'input output param p1 p2'.split():
val = vars()[name]
if val is not None:
setattr(self, name, val)
foo = Foo()
foo.assign(p1=123, p2='abc')
>>> def assign(self, input=None, output=None, param=None, p1=None, p2=None):
... pass
...
很容易理解您应该如何使用该功能:
>>> help(assign)
Python Library Documentation: function assign in module __main__
assign(self, input=None, output=None, param=None, p1=None, p2=None)
相比之下:
>>> def assign2(self, **kwargs):
... pass
...
给出:
>>> help(assign2)
Python Library Documentation: function assign2 in module __main__
assign2(self, **kwargs)
我希望你能理解为什么第一种形式更可取。但是你仍然希望避免把所有东西写两遍(在论点和正文中)
第一个问题是为什么要编写这种性质的代码?我发现这是一种非常常见的情况,我希望一个类有一堆它将携带的属性,但这些属性的集合基本上是固定的。在最常见的情况下,这些属性在对象的生命周期内不会改变;在这种情况下,python有一个内置的帮助程序来实现这一点
>>> import collections
>>> Assignment = collections.namedtuple('Assignment', 'input output param p1 p2')
>>> assign = Assignment(None, None, None, None, None)._replace
>>> assign(p1=10)
Assignment(input=None, output=None, param=None, p1=10, p2=None)
>>> help(Assignment)
Python Library Documentation: class Assignment in module __main__
class Assignment(__builtin__.tuple)
| Assignment(input, output, param, p1, p2)
|
... SNIP
namedtuple
是常规类,您可以从它们继承来赋予它们特殊的行为。不幸的是,它们是不可变的,如果你碰巧需要它,你将需要另一种技术;但您几乎总是应该首先找到命名元组。否则,我们可以利用其他一些魔法;我们可以得到所有局部变量,在函数开始时,这些局部变量只包括参数:
>>> class Assignable(object):
... def assign(self, input=None, output=None, param=None, p1=None, p2=None):
... _kwargs = vars()
... _kwargs.pop('self')
... vars(self).update((attr, value) for attr, value in _kwargs.items() if value is not None)
...
>>> a = Assignable()
>>> vars(a)
{}
>>> a.assign(p1=6)
>>> vars(a)
{'p1': 6}
>>> a.p1
6
而help()
文本仍然非常有用
>>> help(a.assign)
Python Library Documentation: method assign in module __main__
assign(self, input=None, output=None, param=None, p1=None, p2=None) method of __main__.Assignable instance
有了mgilson和unutbu的解决方案,编写所有类型的调用的可能性就消失了 在我的以下解决方案中,保留了这种可能性:
class Buu(object):
def assign(self,
input=None, output=None,
param=None,
p1=None, p2=None,
**kw):
for k,v in locals().iteritems():
if v not in (self,None,kw):
if v == (None,):
setattr(self,k,None)
else:
setattr(self,k,v)
for k,v in kw.iteritems():
setattr(self,k,v)
buu = Buu()
buu.assign(None,(None,), p1=[])
print "buu's attributes:",vars(buu)
print
buu.assign(1,p2 = 555, xx = 'extra')
print "buu's attributes:",vars(buu)
结果
buu's attributes: {'output': None, 'p1': []}
buu's attributes: {'p2': 555, 'output': None, 'xx': 'extra', 'p1': [], 'input': 1}
顺便说一句,当编码人员将None
作为参数的默认参数时,这是因为他预见到在执行过程中没有必要将None
作为重要参数传递。因此,我认为将
None
作为真正的重要参数传递是一个错误的问题。但是,在上面的代码中,通过使用一种约定可以避免这个问题,即如果必须创建一个值为
None
的新属性,则参数应该是(None,)
如果有人想通过(无,)
?真的吗?
哦,见鬼 这正是我所需要的,谢谢你,米吉尔森!如果v:,请小心使用
。这不会为python中未被视为True
的任何值设置属性。因此obj.assign(a=0,b='',c=[])
不会将这些属性中的任何一个分配给obj
。我认为如果v不是None:
会更安全。@WarrenWeckesser--这是一个合理的观点。我这样回答是为了镜像OP的原始代码,但很高兴在这里注意到这一点。对于您的解决方案,必须以关键字形式传递参数。在我的回答中,不是这样。顺便说一下,您的代码有一个潜在的问题:如果您想实际分配值None
?如果这很重要,您可以通过在参数中创建一个sentinel值(例如,sentinel=object()
,然后input=sentinel,output=sentinel,
),然后检查If input不是sentinel
,而不是If input
。@abarnert我明白了。改成**kwargs
方式,我现在不需要关心哨兵。没问题。正如我所说,这只是一个“潜在问题”。如果没有有效的false-y值(None
,0
,'
,[]
,等等),您可能希望存储在属性中,这是非常好的。如果您想重现他的赋值
函数,我为您发布了一个答案,对于任何不在kwargs
中的键,您应该引发TypeError
,而不是跳过它。此外,我认为调用setattr
而不是self更为通用(例如,与\uuuuuuu插槽一起工作),而且可能更为明确。直接触摸dict时,会忽略装饰符。因此,如果一个人使用@property
和@x.setter
来做某事,这将打破这种局面。setattr
调用修复了这个问题(就像abarnert提到的那样)。@BlackPlanet:是的,我只是想举一个例子,说明什么时候需要setattr
。还有一些自定义描述符,\uuuuu setattr\uuuuu
,具有一些相同问题的超类,在C/RPython/Java/.NET中实现的具有类似问题的超类,……请您对我的答案有何看法?@eyquem:我感觉到您试图解决的问题与Firegun提出的问题不同,更复杂。我不相信这个问题的正确性,所以我对解决方案不感兴趣。(我喜欢简单的事情;你的解决方案太复杂了。)然而,我确实喜欢你保留了一个明确的调用签名;因此我更改了答案。添加一个docstring''参数,默认值为None:input、output、param、p1、p2'
和help(assign)
将显示两行assign(self,**kwargs)
和''参数,默认值为None:input、output、para