我正在尝试做这样的金钱类:
from __future__ import annotations;
class Money:
__ZERO = Money(0)
def __init__(self, amount: float):
self.__amount: float = amount
@staticmethod
def ZERO() -> Money:
return __ZERO
@staticmethod
def fromNumber(value: float) -> Money:
return Money(value)
当我尝试导入 money.py 时,我有以下异常:
名称"钱"未定义
文件 "E:\Desenvolvimento\Python\Money\money.py",第 4 行,In Money __ONE = 钱(0(
文件 "E:\Desenvolvimento\Python\Money\money.py",第 3 行,在 类钱:
文件 "E:\Desenvolvimento\Python\Money\money_test.py",第 2 行,在 从货币进口 货币
我来自Java背景,经过一些阅读,我认为这应该可以工作。我是否忘记了什么,或者这样做的方法完全不同?
__ZERO = Money(0)
尚未创建类Money
,因此您还不能创建类型Money
的对象,解决方法是定义您的类,然后稍后分配您的常量:
class Money:
...
@staticmethod
def fromNumber(value: float) -> 'Money':
...
Money.__ZERO = Money(0)
print(Money.__ZERO)
输出:
<__main__.Money object at 0x7ff7c4016c50>
并且,按照@jonrsharpe的建议,您可以将类型注释更改为-> 'Money':
.
正如 MrGeek 在他的回答中诊断的那样,您遇到的问题是,当您初始化类变量时,名称Money
还不存在。在 Python 中,类主体都在创建类对象之前运行,这发生在它可以绑定到外部命名空间之前。
我建议推迟零值的创建,直到您第一次需要使用它。将其初始化为某个哨兵值(如None
(,并在要使用它时检查它是否已设置:
class Money:
__ZERO = None
@staticmethod
def ZERO():
if Money.__ZERO is None:
Money.__ZERO = Money(0)
return Money.__ZERO
请注意,您需要在属性名称的所有用法前面加上类。在 Python 中,方法内部没有对类变量的隐式访问,您必须是显式的。这可能是不使ZERO
成为静态方法的原因。如果你把它做成一个classmethod
,你会得到这个类作为参数传递给方法:
@classmethod
def ZERO(cls):
if cls.__ZERO is None:
cls.__ZERO = cls(0)
return cls.__ZERO
我也不清楚fromNumber
方法的目的是什么,因为它与类的构造函数相同。 与其调用Money.fromNumber(x)
,不如始终只调用Money(x)
。
最后一点:您当前正在为常量类变量使用双下划线前缀,并使用一种方法来访问它。双前导下划线调用了 Python 的名称重整系统,因此就任何外部代码而言,实际的类变量是_Money__ZERO
的。这实际上并不像在其他语言中声明变量private
。相反,当您不希望属性意外与一些未知的其他变量发生冲突时(可能是因为您是代理对象并且不知道要代理的对象将具有哪些方法和变量(。
对于普通类,通常将属性作为公共 API 的一部分或使用单个下划线来指示它是私有的更有意义。没有强制执行该声明,Python 不会尝试保护它免受外部访问。但这通常是调试和测试的一件好事,您希望外部代码能够在内部进行搜索。因此,一种更 Python 的方法可能是:
class Money:
def __init__(self, amount: float):
self._amount: float = amount # no need for name mangling here either
# one underscore indicates the attribute is not part of your public API
Money.ZERO = Money(0) # set the class variable after defining the class
然后以后的代码可以在需要的时候直接获取Money.ZERO
,根本不需要调用任何方法。