如何优雅地将类的所有属性作为参数传递到函数中?

2024-04-29 12:16:33 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个有点复杂的类Thing,以及一个相关联的mixinIterMixin(使类成为可编辑的)…和一个funky方法,它接收我的类的一个实例作为参数。在

事实上,我正试图将一组参数捆绑成单个对象,传递给下面的funky函数之外的多个外部函数。一种参数对象设计模式。。。在

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

关于funky函数,在一个完全不同的模块中,通过Thing类,我想将Thing的属性(foobar)和baz)作为一个参数传递给funky函数,如下所示:

^{pr2}$

问题:

如果我不使属性foobar私有setter(例如,通过self._foo)即下划线的方式——那么我在类初始化期间引发无限递归……因为这些属性的__init__和setter反复循环并反复调用自己。为了避免这种情况,我使用了@property修饰符,并在设置foo和bar时将它们“私有化”。在

但是,当我传递Thing类的实例,并通过splat或asterick将其属性解压为funky函数中的参数,如果我反省这些属性的结果键,我仍然得到_foo_bar。我好像无法摆脱下划线。(换句话说,我得到了Thing的“私有化”属性名。)

funky的biz逻辑需要解压后的值没有任何下划线。在

为什么会发生这种情况(拆包时的下划线)?我该怎么解决这个问题?有没有一种更优雅的方法可以在不私有化任何东西的情况下初始化foo和bar属性?或者用一种更python的方式将Thing类中的所有属性传递给我的funky函数?在


Tags: 函数selfnoneparserdata参数return属性
1条回答
网友
1楼 · 发布于 2024-04-29 12:16:33

首先,您遇到了一个大问题,它甚至会阻止您看到您请求帮助的问题:您的Thing类定义了一个__iter__方法,该方法没有super,也没有yieldreturn任何东西。希望这部分只是一些打字错误,你知道如何修复它来做你真正想做的事情。在

不,你要问的问题是:

class IterMixin():
    def __iter__(self):
        for attr, value in self.__dict__.items():
            yield attr, value

尝试打印出实例的__dict__。或者,最好是这样一个最小的例子:

^{pr2}$

输出是{'_foo': 2}。在

您试图通过给属性指定私有名称并将其放在属性后面来隐藏属性,但是之后您又在属性的背后直接查看了真正属性所在的__dict__。在

还有什么能在那里?实际的_foo必须存储在每个实例的某处。另一方面,foo实际上不是一个值,它是一个使用私有属性的getter/setter,因此它不会存储在任何地方。在

如果您真的希望使用反射来查找实例上的所有“公共值”,可以执行以下操作:

for attr, value in inspect.getmembers(self):
    if not attr.startswith('_') and not callable(value):
        yield attr, value

然而,我认为这将是更好的而不是这样做。更简单、更干净的选项包括:

  • 添加一个_fields = 'foo', 'bar', 'baz',并让基类迭代_fields_。在
  • 编写一个注册属性的decorator,并让基类迭代该注册表。在
  • 构建一些可以让您更具声明性地指定属性并为您编写样板的东西。请参见^{}^{}、和{a3}获取一些灵感。在
  • 只需使用attrs(或者,如果您不是OP,而是将来阅读本文的人,他可以依赖3.7+,dataclass)为您完成这项工作。在
  • 重新考虑你的设计。一个类的实例迭代其公共属性的名称-值对首先是很奇怪的。一个类似于映射的“parameter object”可以用于关键字展开;一个类似于普通iterable的对象可能有用;一个充当名称-值对的iterable对除了传递给dict构造(在这一点上,作为映射更简单)之外,对任何事情都没有用。另外,混音并不能帮你解决困难的部分。Whatever you actually need to do, ask for help on how to do that, instead of how to make this code that shouldn't work work anyway.

相关问题 更多 >