【问题标题】:Automatically setting class member variables in Python在 Python 中自动设置类成员变量
【发布时间】:2010-10-07 18:18:35
【问题描述】:

说,我在 Python 中有以下类

class Foo(object):
    a = None
    b = None
    c = None
    def __init__(self, a = None, b = None, c = None):
        self.a = a
        self.b = b
        self.c = c

有没有办法简化这个过程?每当我向 Foo 类添加新成员时,我都不得不修改构造函数。

【问题讨论】:

标签: python constructor


【解决方案1】:

请注意

class Foo(object):
    a = None

Foo的字典中设置一个键值对:

Foo.__dict__['a']=None

同时

def __init__(self, a = None, b = None, c = None):
    self.a = a

在 Foo 实例对象的字典中设置一个键值对:

foo=Foo()
foo.__dict__['a']=a

因此,在定义的顶部设置类成员与在定义的下半部分(__init__ 内)设置实例属性没有直接关系。

另外,很高兴知道__init__ 是Python 的初始化程序__new__ 是类构造函数


如果您正在寻找一种基于__init__ 的参数自动添加一些实例属性的方法,您可以使用这个:

import inspect
import functools

def autoargs(*include,**kwargs):   
    def _autoargs(func):
        attrs,varargs,varkw,defaults=inspect.getargspec(func)
        def sieve(attr):
            if kwargs and attr in kwargs['exclude']: return False
            if not include or attr in include: return True
            else: return False            
        @functools.wraps(func)
        def wrapper(self,*args,**kwargs):
            # handle default values
            for attr,val in zip(reversed(attrs),reversed(defaults)):
                if sieve(attr): setattr(self, attr, val)
            # handle positional arguments
            positional_attrs=attrs[1:]            
            for attr,val in zip(positional_attrs,args):
                if sieve(attr): setattr(self, attr, val)
            # handle varargs
            if varargs:
                remaining_args=args[len(positional_attrs):]
                if sieve(varargs): setattr(self, varargs, remaining_args)                
            # handle varkw
            if kwargs:
                for attr,val in kwargs.iteritems():
                    if sieve(attr): setattr(self,attr,val)            
            return func(self,*args,**kwargs)
        return wrapper
    return _autoargs

所以当你说

class Foo(object):
    @autoargs()
    def __init__(self,x,path,debug=False,*args,**kw):
        pass
foo=Foo('bar','/tmp',True, 100, 101,verbose=True)

你会自动获得这些实例属性:

print(foo.x)
# bar
print(foo.path)
# /tmp
print(foo.debug)
# True
print(foo.args)
# (100, 101)
print(foo.verbose)
# True

附言。虽然我写了这个(为了好玩),但我不建议将autoargs 用于严肃的工作。明确是简单、清晰和无误的。我不能对autoargs说同样的话。

PPS。只是我,还是 Stackoverflow 上有很多按钮坏了?编辑器窗口丢失了所有图标... :(清除浏览器缓存解决了问题。

【讨论】:

  • 我忘了添加 - 感谢您清理我的 Python 术语。
  • @Jeeyoung:没问题;很高兴我能帮上忙。
  • 这不应该在标准库中吗?我没有看到在每个 init... 上进行设置的意义
【解决方案2】:

elegant 方法可以做到这一点。

有没有办法简化这个过程?每当我向 Foo 类添加新成员时,我都不得不修改构造函数。

还有一个粗略的方式。它会起作用,但不推荐。看看再决定。

>>> class Foo(object):
    def __init__(self, **attrs):
        self.__dict__.update(**attrs)
    def __getattr__(self, attr):
        return self.__dict__.get(attr, None)


>>> f = Foo(a = 1, b = 2, c = 3)
>>> f.a, f.b
(1, 2)
>>> f = Foo(bar = 'baz')
>>> f.bar
'baz'
>>> f.a
>>> 

关键字参数构造函数让您无需显式定义任何参数即可摆脱困境。 警告这违背了“显式优于隐式”的原则

仅当您想为不存在的属性返回默认值而不是获取AttributeError 时,您才需要覆盖__getattr__

【讨论】:

  • 完美!最优雅的方式来做到这一点。
【解决方案3】:

Python 3.7 提供了dataclasses,这在以下情况下很有帮助:

from dataclasses import dataclass


@dataclass
class Foo:
    a: str = None
    b: str = None
    c: str = None

当您只想存储一些属性时,这使您不必写出__init__ 方法。

给你一个很好的__repr__方法:

>>> a = Foo()
>>> a
Foo(a=None, b=None, c=None)

如果需要对某个参数进行计算,可以实现__post_init__

另见命名元组:

from collections import namedtuple

Foo = namedtuple('Foo', ['a', 'b', 'c'])

namedtuple 需要所有字段。

>>> a = Foo(1, 2, 3)
>>> a
Foo(a=1, b=2, c=3)

【讨论】:

    【解决方案4】:
    猜你喜欢
    • 2013-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-26
    • 1970-01-01
    相关资源
    最近更新 更多