python:不可变的私有类变量?

2024-04-24 15:30:05 发布

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

有没有办法把这个Java代码转换成Python?

class Foo
{
    final static private List<Thingy> thingies = 
       ImmutableList.of(thing1, thing2, thing3);
}

例如,thingies是属于Foo类而不是其实例的Thingy对象的不可变私有列表。

我知道如何从这个问题Static class variables in Python定义静态类变量,但我不知道如何使它们不可变和私有。


Tags: of代码foostaticjavaprivatelistclass
3条回答

通过使用属性可以使它不可写(与不可变有细微的不同),但是没有办法使它私有化——这违背了Python的哲学。

class Foo(object):    # don't need 'object' in Python 3
    @property
    def thingies(self):
        return 'thing1', 'thing2', 'thing3'

f = Foo()
print f.thingies
#('thing1', 'thing2', 'thing3')
f.thingies = 9
#Traceback (most recent call last):
#  File "test.py", line 8, in <module>
#    f.thingies = 9
#AttributeError: can't set attribute

它是否是不可变的取决于您返回的内容;如果您返回一个可变的对象,您可能能够对进行变异,使这些更改显示在实例/类中。

class FooMutable(object):
    _thingies = [1, 2, 3]
    @property
    def thingies(self):
        return self._thingies

foo = FooMutable()
foo.thingies.append(4)
print foo.thingies
# [1, 2, 3, 4]

这将允许您对thingies进行变异,并且由于返回的对象与实例/类中保留的对象相同,因此更改将反映在后续访问中。

比较一下:

class FooMutable(object):
    @property
    def thingies(self):
        return [1, 2, 3]

foo = FooMutable()
foo.thingies.append(4)
print foo.thingies
# [1, 2, 3]

因为每次都会返回一个全新的列表,所以对它的更改不会反映在后续的访问中。

在Python中,惯例是在属性名上使用_前缀表示protected,使用__前缀表示private。这不是由语言强制的;程序员应该知道不要编写依赖于非公共数据的代码。

如果您真的想强制执行不变性,那么可以使用元类[docs](类的类)。只要修改__setattr____delattr__以在有人试图修改它时引发异常,并将其设为tuple(不可变列表)[docs

class FooMeta(type):
    """A type whose .thingies attribute can't be modified."""

    def __setattr__(cls, name, value):
        if name == "thingies":
            raise AttributeError("Cannot modify .thingies")
        else:
            return type.__setattr__(cls, name, value)

    def __delattr__(cls, name):
        if name == "thingies":
            raise AttributeError("Cannot delete .thingies")
        else:
            return type.__delattr__(cls, name)

thing1, thing2, thing3 = range(3)

class Foo(object):
    __metaclass__ = FooMeta
    thingies = (thing1, thing2, thing3)
    other = [1, 2, 3]

示例

print Foo.thingies # prints "(0, 1, 2)"
Foo.thingies = (1, 2) # raises an AttributeError
del Foo.thingies # raise an AttributeError
Foo.other = Foo.other + [4] # no exception
print Foo.other # prints "[1, 2, 3, 4]"

从技术上讲,仍然可以通过类的内部.__dict__属性来修改这些属性,但这应该足以阻止大多数用户,很难完全保护Python对象。

你不能在Python中做这两件事,至少在Java中不能。

按照惯例,以下划线为前缀的名称被视为私有名称,不应在实现外部访问,但Python中的任何内容都不强制执行此约定。这被认为是一个警告,说明您正在处理一个实现细节,在未来的代码版本中可能会在没有警告的情况下发生更改。

相关问题 更多 >