[英]How to Elegantly Pass All Attributes of a Class as Arguments in a Function?
I have a somewhat complex class Thing
, and an associated mixin IterMixin
(to make the class iterable)...and a funky
method elsewhere in the codebase which receives an instance of my class as an argument. 我有一个稍微复杂的类
Thing
,以及一个关联的mixin IterMixin
(使该类可迭代)...以及代码库中其他位置的funky
方法,该方法接收我的类的实例作为参数。
In fact, I'm attempting to bundle up a bunch of parameters as single object to be passed to multiple external functions beyond the funky
function below. 实际上,我正在尝试将一堆参数捆绑为一个对象,以传递给下面的
funky
函数之外的多个外部函数。 A parameter object design pattern of sorts... 各种参数对象设计模式...
class IterMixin():
def __iter__(self):
for attr, value in self.__dict__.items():
yield attr, value
class Thing(IterMixin):
def __iter__(self, foo=None, bar=None, baz=999):
if foo is None:
self.foo = {}
else:
self.foo = foo
if bar is None:
self.foo = {}
else:
self.bar = bar
self.baz = baz
@property
def foo(self):
return self._foo
@foo.setter
def foo(self, data)
self._foo = self.parser(data)
@property
def bar(self):
return self._bar
@bar.setter
def bar(self, more_data)
self._bar, self.baz = self.another_parser(more_data)
def parser(self, data):
...do stuff...
return foo
def another_parser(self, more_data):
...do add'l stuff...
return bar, baz
With regard to the funky
function, in a completely different module, via the Thing
class, I want to pass Thing
's attributes ( foo
, bar
, and baz
) to the funky
function as one argument ...like so: 关于
funky
函数,在一个完全不同的模块中,通过Thing
类,我想将Thing
的属性( foo
, bar
和baz
)作为一个参数传递给funky
函数,就像这样:
thing_args = Thing()
def funky(*thing_args):
...do stuff...
...expecting to manipulate keys from things_arg
...
return whatever
PROBLEM: 问题:
If I do not make the setters for the attributes foo
and bar
private (for example, via self._foo
)--ie, by way of an underscore--then I evoke infinite recursion during class initialization ...as the __init__
and setters for these attributes loop over and over and repeatedly call themselves. 如果我不将
foo
和bar
属性的设置器 self._foo
私有 (例如,通过self._foo
)(即通过下划线),那么我将在类初始化期间调用无限递归...就像__init__
和setters一样这些属性会不断循环并反复调用自己。 To avoid that, I used the @property
decorator and "privatized" the foo and bar while setting them. 为了避免这种情况,我在设置它们时使用了
@property
装饰器,并对foo和bar进行了“私有化”。
However, when I pass an instance of the Thing
class, and unpack its attributes as args in the funky
function via a splat or asterick, if I introspect the resultant keys for those attributes, I still get _foo
and _bar
. 但是,当我传递
Thing
类的实例并通过splat或asterick在funky
函数中将其属性解压缩为args时,如果我对这些属性的结果键进行内省,则仍然会得到_foo
和_bar
。 I can't seem to get rid of the underscores. 我似乎无法摆脱下划线。 (In other words, I get the "privatized" attribute names of
Thing
.) (换句话说,我得到
Thing
的“私有化”属性名称。)
The biz logic of funky
needs the unpacked values to not have any underscores. funky
的biz逻辑需要解包的值没有下划线。
Why is this happening (the underscores upon unpacking)? 为什么会发生这种情况(打开包装时要加下划线)? How can I fix this?
我怎样才能解决这个问题? Is there a more elegant way to either initialize the foo and bar attributes without privatizing anything?
有没有一种更优雅的方式来初始化foo和bar属性而不私有化任何东西? Or perhaps a more Pythonic way to pass all the attributes in the
Thing
class to my funky
function? 还是将Python
Thing
类中的所有属性传递给我的funky
函数的一种更Python化的方法?
First, you've got a major problem that will prevent you from even seeing the problem you've asked for help with: Your Thing
class defines an __iter__
method that doesn't super
, and doesn't yield
or return
anything. 首先,您遇到了一个主要问题,该问题将使您什至看不到您寻求帮助的问题:
Thing
类定义了一个__iter__
方法,该方法不是super
,也不yield
或return
任何东西。 Hopefully that part is just some typo and you know how to fix it to do whatever you actually wanted there. 希望那部分只是一些错字,并且您知道如何对其进行修复以执行您实际想要的任何操作。
No, onto the problem you're asking about: 不,要解决的问题是:
class IterMixin():
def __iter__(self):
for attr, value in self.__dict__.items():
yield attr, value
Try printing out the __dict__
of your instances. 尝试打印实例的
__dict__
。 Or, better, instances of a minimal example like this: 或者更好的是,像这样的最小示例的实例:
class Thing:
@property
def foo(self):
return self._foo
@foo.setter
def foo(self, data):
self._foo = data
t = Thing()
t.foo = 2
print(t.__dict__)
The output is {'_foo': 2}
. 输出为
{'_foo': 2}
。
You've tried to hide the attributes by giving them private names and putting them behind properties, but then you've gone around behind the properties' backs and looked directly into the __dict__
where the real attributes are. 您试图通过给它们提供私有名称并将其放在属性后面来隐藏属性,但是随后您又绕过了属性的背后,直接查看了真正属性所在的
__dict__
。
And what else could be there? 而且还有什么可以呢? Your actual
_foo
has to be stored somewhere on each instance. 您实际的
_foo
必须存储在每个实例的某个位置 。 That foo
, on the other hand, isn't really a value, it's a getter/setter that uses that private attribute, so it isn't stored anywhere. 另一方面,
foo
并不是一个真正的值,它是使用私有属性的getter / setter方法,因此它不存储在任何地方。
If you really want to use reflection to find all of the "public values" on an instance, you can do something like this: 如果您真的想使用反射来查找实例上的所有“公共值”,则可以执行以下操作:
for attr, value in inspect.getmembers(self):
if not attr.startswith('_') and not callable(value):
yield attr, value
However, I think it would be much better to not do this reflectively. 但是,我认为最好不要进行反思。 Simpler and cleaner options include:
更简单,更清洁的选项包括:
_fields = 'foo', 'bar', 'baz'
and have the base class iterate _fields_
. _fields = 'foo', 'bar', 'baz'
并让基类迭代_fields_
。 namedtuple
, dataclass
, and attrs
for some inspiration. namedtuple
, dataclass
和attrs
以获得一些启发。 attrs
(or, if you're not the OP but someone reading this from the future who can rely on 3.7+, dataclass
) to do that work for you. attrs
(或者,如果你不是OP但有人从未来谁可以依靠3.7+,阅读本dataclass
),这样做对你的工作。 dict
construct (at which point it's, again, simpler to be a mapping). dict
构造(在这一点上,再次成为映射更简单)。 Plus, a mixin is really not helping you with the hard part of doing it.
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.