76

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

我正在创建一个对象的快照(如果出现任何故障,用于恢复);一旦分配了此备份变量,就不应对其进行修改——Python 中的类似 final 的功能对此会很好。

4

11 回答 11

71

在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
}

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

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

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

于 2009-04-29T14:32:44.383 回答
71

finalPython 中没有等价物。

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

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

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

        self.__dict__[attr] = value
于 2009-04-29T14:30:34.640 回答
50

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 警告您有关不正确的类型使用。

于 2019-10-14T22:20:11.027 回答
10

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

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

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

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

于 2009-04-29T14:41:33.397 回答
7

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

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

于 2009-04-29T14:29:04.690 回答
6

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

于 2009-04-29T14:28:29.950 回答
6

您可以通过描述符协议模拟类似的东西,因为它允许以您希望的方式定义读取和设置变量。

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
于 2009-06-30T10:41:27.897 回答
6

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

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

于 2019-09-03T14:59:31.853 回答
3

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

不过,我会考虑让它保持可变。

于 2009-04-29T14:29:05.500 回答
1

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"
于 2018-08-10T12:20:36.873 回答
0

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

PI = 3.14
radius = 3

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

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

于 2013-09-07T05:29:23.090 回答