在Python中,在对象创建时设置类属性的最佳方法是什么?

2024-05-15 05:14:33 发布

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

当我定义一个类时,我经常希望在创建对象时为该类设置一个属性集合。到目前为止,我是通过将属性作为参数传递给init方法来实现的。然而,我对这种代码的重复性感到不满:

class Repository(OrderedDict,UserOwnedObject,Describable):
  def __init__(self,user,name,gitOriginURI=None,gitCommitHash=None,temporary=False,sourceDir=None):
    self.name = name
    self.gitOriginURI = gitOriginURI
    self.gitCommitHash = gitCommitHash
    self.temporary = temporary
    self.sourceDir = sourceDir
    ...

在这个例子中,我必须输入name三次,gitOriginURI三次,gitCommitHash三次,temporary三次,和sourceDir三次。只是为了设置这些属性。这是非常无聊的代码写。在

我曾考虑过这样的改变课程:

^{pr2}$

初始化他们的对象,比如:

f = Foo()
f.a = whatever
f.b = something_else
f.c = cheese

但从文档的角度来看,这似乎更糟,因为类的用户需要知道需要设置哪些属性,而不是简单地查看自动生成的help()字符串作为类的初始值设定项。在

有更好的方法吗?在

有一件事我认为可能是一个有趣的解决方案,那就是如果有一个store_args_to_self()方法,它将把传递给init的每个参数作为self的属性存储起来。有这样的方法吗?在

有一件事让我对寻找更好的方法感到悲观,那就是在cPython的源代码中查看date对象的源代码,例如,我看到了相同的重复代码:

def __new__(cls, year, month=None, day=None):
    ...
    self._year = year
    self._month = month
    self._day = day

https://github.com/python/cpython/blob/master/Lib/datetime.py#L705

而urwid,虽然使用setter稍微有些模糊,但也有这样一个“接受参数并将其设置为self的属性”的烫手山芋代码:

def __init__(self, caption=u"", edit_text=u"", multiline=False,
        align=LEFT, wrap=SPACE, allow_tab=False,
        edit_pos=None, layout=None, mask=None):
    ...

    self.__super.__init__("", align, wrap, layout)
    self.multiline = multiline
    self.allow_tab = allow_tab
    self._edit_pos = 0
    self.set_caption(caption)
    self.set_edit_text(edit_text)
    if edit_pos is None:
        edit_pos = len(edit_text)
    self.set_edit_pos(edit_pos)
    self.set_mask(mask)

https://github.com/urwid/urwid/blob/master/urwid/widget.py#L1158


Tags: 方法代码textnameposselfnone属性
3条回答

好吧,你可以这样做:

class Foo:
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)

foo = Foo(a=1, b='two', c='iii')
print(foo.a, foo.b, foo.c)

输出

^{pr2}$

但是如果这样做了,那么在将kwargs中的键转储到实例__dict__之前,最好检查一下它们是否正常。;)

下面是一个稍微有点花哨的示例,它对传入的参数进行了一点检查。在

class Foo:
    attrs = ['a', 'b', 'c']
    ''' Some stuff about a, b, & c '''
    def __init__(self, **kwargs):
        valid = {key: kwargs.get(key) for key in self.attrs}
        self.__dict__.update(valid)

    def __repr__(self):
        args = ', '.join(['{}={}'.format(key, getattr(self, key)) for key in self.attrs])
        return 'Foo({})'.format(args)

foo = Foo(a=1, c='iii', d='four')
print(foo)

输出

Foo(a=1, b=None, c=iii)

您可以使用^{} project让它为您生成__init__方法;它还将负责表示、哈希和等式测试(以及可选的丰富比较和不变性):

from dataclasses import dataclass
from typing import Optional

@dataclass
class Repository(OrderedDict, UserOwnedObject, Describable):
    name: str
    gitOriginURI: Optional[str] = None
    gitCommitHash: Optional[str] = None
    temporary: bool = False
    sourceDir: Optional[str] = None

dataclasses是在PEP 557 - Data Classes中定义的,python3.7中已经接受了这一点。该库将在python3.6及更高版本上运行(因为它依赖于3.6中引入的新的变量注释语法)。在

该项目的灵感来自于^{} project,它提供了更多的灵活性和选项,并与python2.7和python3.4及更高版本兼容。在

对于python2.7,我的解决方案是从namedtuple继承并使用namedtuple本身作为init的唯一参数。为了避免每次使用decorator时都重载new。其优点是我们有显式的init签名w/o*args、**kwargs,因此,有很好的IDE建议

def nt_child(c):
    def __new__(cls, p): return super(c, cls).__new__(cls, *p)
    c.__new__ = staticmethod(__new__)
    return c

ClassA_P = namedtuple('ClassA_P', 'a, b, foo, bar')

@nt_child
class ClassA(ClassA_P):
    def __init__(self, p):
        super(ClassA, self).__init__(*p)
        self.something_more = sum(p)

a = ClassA(ClassA_P(1,2,3,4)) # a = ClassA(ClassA_P( <== suggestion a, b, foo, bar
print a.something_more # print a. <== suggesion a, b, foo, bar, something_more

相关问题 更多 >

    热门问题