【问题标题】:`final` keyword equivalent for variables in Python?Python中变量的“final”关键字等效?
【发布时间】:2010-10-22 14:25:56
【问题描述】:

我在 Python 中找不到与 Java 的 final 等效的文档,有这样的东西吗?

我正在创建一个对象的快照(用于在任何失败时进行恢复);一旦分配了这个备份变量,就不应对其进行修改——Python 中类似 final 的特性会很好。

【问题讨论】:

  • 总的来说..缺乏不变性是灾难性的..证明Python不适合大型多线程应用程序。
  • @ApurvaSingh 虽然我同意 Python 不适合这样做,但标准库中有元组和frozenset,MagicStack/immutables 是可变映射的实现,建议将其添加到标准库也是如此。

标签: java python keyword final


【解决方案1】:

Python 没有“final”的等价物。除了命名约定外,它也没有“公共”和“受保护”。不是那种“束缚和纪律”。

【讨论】:

    【解决方案2】:

    没有这样的事情。总的来说,Python 的态度是“如果你不想修改,就不要修改它”。无论如何,API 的客户不太可能只是在您未记录的内部结构中闲逛。

    我想,您可以通过对模型的相关位使用元组或命名元组来解决此问题,这些位本质上是不可变的。当然,这仍然无助于模型中必须可变的任何部分。

    【讨论】:

    • 许多其他类也是不可变的:字符串、整数、长整数、浮点数。
    【解决方案3】:

    http://code.activestate.com/recipes/576527/ 定义了一个 freeze 函数,虽然它不能完美地工作。

    我会考虑让它保持可变。

    【讨论】:

      【解决方案4】:

      Python 中没有 final 等价物。

      但是,要创建类实例的只读字段,您可以使用property 函数。

      编辑:也许你想要这样的东西:

      class WriteOnceReadWhenever:
          def __setattr__(self, attr, value):
              if hasattr(self, attr):
                  raise Exception("Attempting to alter read-only value")
      
              self.__dict__[attr] = value
      

      【讨论】:

      • 我总是用return super(WriteORW, self).__setattr__(attr, value)替换self.__dict__[attr] = value
      【解决方案5】:

      Java 中的变量为final 基本上意味着一旦分配给变量,就不能重新分配该变量以指向另一个对象。这实际上并不意味着该对象不能被修改。例如,以下 Java 代码运行良好:

      public final List<String> messages = new LinkedList<String>();
      
      public void addMessage()
      {
          messages.add("Hello World!");  // this mutates the messages list
      }
      

      但以下内容甚至无法编译:

      public final List<String> messages = new LinkedList<String>();
      
      public void changeMessages()
      {
          messages = new ArrayList<String>();  // can't change a final variable
      }
      

      所以你的问题是关于final 是否存在于 Python 中。它没有。

      但是,Python 确实具有不可变的数据结构。例如,虽然您可以修改 list,但不能修改 tuple。你可以改变set,但不能改变frozenset等。

      我的建议是不要担心在语言级别强制执行非变异,而只需专注于确保您不会编写任何代码在分配这些对象后对其进行变异。

      【讨论】:

      • 在 Java 中,一个非原始变量指向一个对象。将变量指定为 final 意味着您不能使变量指向不同的对象,但是被指向的对象可以被改变。
      • final在Java中还有一个用处,我觉得和C++中的static一样,就是赋值给final变量的那行代码不会执行多次,甚至如果在函数内部声明。它允许您在代码的其余部分中隐藏变量,而无需在每次调用函数时不断构造新实例。在 Python 中,您必须在全局范围内使用变量或接受不断构建新实例的成本。 cpp.sh/24gj
      • 我觉得对simply concentrate on making sure that you don't write any code which mutates these objects after they're assigned 的建议很糟糕。你将如何与其他人沟通?还是对未来的自己?带有注释或文档字符串?正式的语言结构比注释的波动性要小得多。 Python 缺少这一点确实令人担忧。
      • 对于 >= 3.8 的版本不再准确。请参阅下面的@congusbongus 答案。
      【解决方案6】:

      一次赋值变量是一个设计问题。您设计应用程序的方式是只设置一次变量。

      但是,如果您想对设计进行运行时检查,可以使用对象周围的包装器来完成。

      class OnePingOnlyPleaseVassily(object):
          def __init__(self):
              self.value = None
          def set(self, value):
              if self.value is not None:
                  raise Exception("Already set.")
              self.value = value
      
      someStateMemo = OnePingOnlyPleaseVassily()
      someStateMemo.set(aValue) # works
      someStateMemo.set(aValue) # fails
      

      这很笨重,但它会在运行时检测设计问题。

      【讨论】:

        【解决方案7】:

        您可以通过descriptor protocol 模拟类似的东西,因为它允许按照您希望的方式定义读取和设置变量。

        class Foo(object):
        
          @property
          def myvar(self):
             # return value here
        
          @myvar.setter
          def myvar(self, newvalue):
             # do nothing if some condition is met
        
        a = Foo()
        print a.myvar
        a.myvar = 5 # does nothing if you don't want to
        

        【讨论】:

          【解决方案8】:

          虽然这是一个老问题,但我想我会添加另一个可能的选项:您也可以使用assert 来验证变量是否设置为您最初打算设置的内容 - 仔细检查您是否愿意.虽然这和Java中的final不一样,但是可以用来创建类似的效果:

          PI = 3.14
          radius = 3
          
          try:
              assert PI == 3.14
              print PI * radius**2
          except AssertionError:
              print "Yikes."
          

          如上所示,如果PI 出于某种原因未设置为3.14,则会抛出AssertionError,因此try/except 块可能是一个明智的添加。无论如何,根据您的情况,它可能会派上用场。

          【讨论】:

            【解决方案9】:

            Python 确实没有最终类型,它确实有不可变类型,例如元组,但那是另外一回事。

            这里的其他一些答案使类充满了伪最终变量,我更喜欢我的类只有几个最终类型,所以我建议使用描述符来创建最终类型:

            from typing import TypeVar, Generic, Type
            
            T = TypeVar('T')
            
            class FinalProperty(Generic[T]):
                def __init__(self, value: T):
                    self.__value = value
                def __get__(self, instance: Type, owner) -> T:
                    return self.__value
                def __set__(self, instance: Type, value: T) -> None:
                    raise ValueError("Final types can't be set")
            

            如果你像这样使用这个类:

            class SomeJob:
                FAILED = FinalProperty[str]("Failed")
            

            那么您将无法在该类的任何实例中设置该变量。 不幸的是,与 WriteOnceReadWhenever 答案一样,您仍然可以设置类变量。

            job = SomeJob()
            job.FAILED = "Error, this will trigger the ValueError"
            SomeJob.FAILED = "However this still works and breaks the protection afterwards"
            

            【讨论】:

              【解决方案10】:

              截至 2019 年和 PEP 591,Python 具有 Final 类型。在 Python 3.8 发布之前,它不会在标准库中可用,但在此之前,您可以通过 typing-extensions 库使用它。它不会像 final 在 Java 中那样工作,尽管 Python 仍然是一种动态类型的语言。但是,如果您将它与像 mypy 这样的静态类型检查器一起使用,它会给您带来非常相似的好处。

              还有一个final 装饰器可用于将类方法标记为最终方法并防止被覆盖。同样,这仅在“编译时”进行检查,因此您需要在工作流程中包含静态类型检查器。

              【讨论】:

                【解决方案11】:

                Python 3.8(通过PEP 591)添加Final变量、函数、方法和类。以下是一些使用方法:

                @final装饰器(类、方法)

                from typing import final
                
                @final
                class Base:
                    # Cannot inherit from Base
                
                class Base:
                    @final
                    def foo(self):
                        # Cannot override foo in subclass
                

                Final注解

                from typing import Final
                
                PI: Final[float] = 3.14159     # Cannot set PI to another value
                KM_IN_MILES: Final = 0.621371  # Type annotation is optional
                
                class Foo:
                    def __init__(self):
                        self.bar: Final = "baz"   # Final instance attributes only allowed in __init__
                

                请注意,与其他键入提示一样,这些提示不会阻止您覆盖类型,但它们确实有助于 linter 或 IDE 警告您有关不正确的类型使用。

                【讨论】:

                • 我认为这个答案具有误导性,因为很容易让人觉得最终限定符会阻止在运行时重新分配或覆盖,但事实并非如此。
                • @antonagestam 这是一项语言功能。如果您尝试重新分配最终实例,则诸如 Visual Studio Code 和 PyCharm 之类的 IDE 可能会适应并自动显示警告。当您尝试在类外部使用带有下划线前缀的实例变量时,情况已经如此。
                • @momo 就像关于私有属性的问题应该有一个答案来解释 Python 中没有什么是真正私有的,我认为这个问题的答案建议使用 final/Final qualifiers 应该说明它们在运行时什么都不做。请注意,我也回答了这个问题。
                • 我同意@antonagestam 的评论。 @typing.final 仅由静态类型检查器使用。答案应该更清楚地表明,在执行脚本时,这个装饰器完全被忽略了。动态创建的类会默默地忽略这个装饰器。
                猜你喜欢
                • 2011-07-24
                • 1970-01-01
                • 2019-06-13
                • 1970-01-01
                • 1970-01-01
                • 2012-12-30
                • 1970-01-01
                • 2016-10-14
                • 2010-09-05
                相关资源
                最近更新 更多