类变量的设置器

时间:2019-04-11 23:31:49

标签: python class attributes setter

我在名为foo.py的模块中有一个简单的类:

class Foo
    foo = 1
我导入其他模块(bar.pybaz.py)中的

,并在其他模块中更改类变量foo,例如:

# bar.py

from foo import Foo
print(Foo.foo) # should print 1.
Foo.foo = 2

# baz.py

from foo import Foo

print(Foo.foo) # should print 2.
Foo.foo = 3

在设置之前,应先检查对Foo.foo的更改。因此,我目前在Foo中使用setter方法:

# foo.py

@classmethod
def set_foo(cls, new_foo):
    # do some checks on the supplied new_foo, then set foo.
    cls.foo = new_foo

这是设置类变量的pythonic方法吗?还是有更好的方法(类似于实例变量的@property a@a.setter声明)?我希望在其他模块中导入foo时类变量Foo可以保留,并且我真的不想创建Foo的实例,因为我想这更多的是类。 / p>

感谢SO;-)

3 个答案:

答案 0 :(得分:1)

如果您不介意魔术,可以通过使用描述符协议相对合理地完成。

class FooDescriptor:

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        return obj._foo

    def __set__(self, obj, value):
        if not isinstance(obj, type):
            # disable instance name shadowing for sanity's sake
            raise AttributeError("this attribute should be set on the class object")
        obj._foo = value + "!!"


class FooMeta(type):

    foo = FooDescriptor()

    def __new__(cls, clsname, bases, namespace):
        # pluck the "foo" attr out of the class namespace,
        # and swap in our descriptor in its place
        namespace["_foo"] = namespace.pop("foo", "(default foo val)")
        namespace["foo"] = FooMeta.foo
        return type.__new__(cls, clsname, bases, namespace)

在构造类Foo时,这将用数据描述符替换常规声明方式定义的foo类属性(以提供自定义的getter和setter)。我们将在Foo._foo中存储原始的“非托管”值。

演示:

>>> class Foo(metaclass=FooMeta): 
...     foo = "foo0" 
... 
>>> obj = Foo() 
>>> obj.foo  # accessible from instance, like a class attr
'foo0'
>>> Foo.foo  # accessible from class
'foo0'
>>> Foo.foo = "foo1"  # setattr has magic, this will add exclams
>>> obj.foo
'foo1!!'
>>> Foo.foo
'foo1!!'
>>> vars(obj)  # still no instance attributes
{}
>>> type(Foo).foo  # who does the trick?
<__main__.FooDescriptor at 0xcafef00d>
>>> obj.foo = "boom"  # prevent name shadowing (optional!)
AttributeError: this attribute should be set on the class object

答案 1 :(得分:0)

如果您打算从Foo继承,请注意classmethod将修改调用实例的类的属性。

class Base:

    class_variable = 'base'

    @classmethod
    def set_cvar(cls, value):
        cls.class_variable = value

class Derived(Base):

    pass

Derived.set_cvar('derived')
print(Base.class_variable)
print(Derived.class_variable)

输出:

base
derived

这可能(可能会)或可能不是您想要的。另一种方法是改为使用staticmethod并为类明确命名。

不过,总的来说,我认为这是一种好方法。

答案 2 :(得分:-2)

我认为“ pythonic方式”(如果有)使用_格式使用a.setter:

@property
def bar(self):
    return self._bar

@bar.setter
def environment(self, bar):
    # do some checks on the supplied bar, then set
    self._bar = bar

通过这种方式,_bar是“专用”的,您可以在设置代码时在代码中“禁止”。