【问题标题】:python: immutable private class variables?python:不可变的私有类变量?
【发布时间】:2011-11-16 10:36:57
【问题描述】:

有什么办法可以将这段 Java 代码翻译成 Python 吗?

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

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

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

【问题讨论】:

  • 没有像 Java 中那样的私有变量的内置概念(这不是 pythonic),只有人们倾向于遵守的约定(但仍然不一样):docs.python.org/tutorial/classes.html#private-variables
  • Python 引用。 我们都是绅士对吧?
  • 我认为这是“我们都是成年人”。但这一点非常非常重要。源是可见的。大惊小怪地谈论“隐私”并不是很有帮助。
  • @JakobBowyer 不,这里有些人是女性

标签: python oop class static


【解决方案1】:

您可以使用类型提示*实现final 部分。正如其他人所说,__ 很好地实现了private 方面,所以

from typing import List
from typing_extensions import Final

class Foo:
    __thingies: Final[List[Thingy]] = ImmutableList.of(thing1, thing2, thing3)

我将把ImmutableList 的定义留给你。 tuple 可能会做。

*通常需要注意的是用户可以忽略它们

【讨论】:

    【解决方案2】:

    在 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 对象非常困难。

    【讨论】:

    • +1。我将添加一个警告:仅仅因为 Python 允许你这样做并不意味着你应该这样做。好的 Java 不一定是好的 Python(反之亦然)。
    • @StevenRumbalski:当然。我在帖子中添加了这样的免责声明。
    • 当你可以使用属性时严重的矫枉过正。
    • @EthanFurman:使用property 的答案是保护实例属性Foo().thingies,他要求保护类属性Foo.thingies。我也可以在这里使用属性,但是很多人不了解元类,我希望有一个相对简单的示例。
    • 我不确定这是一个很好的例子,因为thingies 仍然可以在实例上反弹。但是,使用属性(如您所说),thingies 的类属性可以反弹——显然完整的解决方案是元类和属性的组合。
    【解决方案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]
    

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

    【讨论】:

    • 或者只是将_thingies 设为一个元组。
    • @Steven 如果你把myinstance._thingies = None 设为元组,我仍然可以这样做。
    • @Steven,你的意思是像第一个例子? ;)
    【解决方案4】:

    您想查看property() 函数。它允许您为类的成员属性定义自己的自定义 Getter 和 Setter。它可能看起来像这样:

    class myClass(object):
      _x = "Hard Coded Value"
    
      def set_x(self, val): return
    
      def get_x(self): return self._x
    
      def del_x(self): return
    
      x = property(get_x, set_x, del_x, "I'm an immutable property named 'x'")
    

    我没有充分使用它来确定它是否可以用来创建“私人”的东西,所以你必须自己深入研究,但isinstance 可能会有所帮助。

    【讨论】:

      【解决方案5】:

      你不能在 Python 中做任何这些事情,在某种意义上你在 Java 中做不到。

      按照约定,带有下划线前缀的名称被认为是私有的,不应在实现之外访问,但 Python 中没有强制执行此约定。更多地被认为是一个警告,即您正在搞乱一个实现细节,该实现细节可能会在未来版本的代码中更改而不会发出警告。

      【讨论】:

      • 不变性可以通过使用property()来实现。理论上,私有变量可以通过相同的方式实现,方法是让 getter 执行一些检查以确定它是否是一个实例,并根据检查返回一些可接受的默认值来代替实际值。至少,我认为这是可能的。我从来没有发现需要这样做...
      • 仅仅为了获得一些非惯用的并且被视为不必要的东西将是大量的工作。只是前缀是_
      • 属性只能在实例上设为只读,因此您必须在元类上定义它们才能在类上获得所需的行为。而且您仍然可以更改它们;您只需要在元类上更改它们。
      猜你喜欢
      • 2011-10-21
      • 1970-01-01
      • 2012-06-04
      • 2020-12-11
      • 2015-09-25
      • 1970-01-01
      • 2016-06-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多