我绝对是Python的新手(我来自Java),我对类字段有以下疑问。
考虑如下代码:
class Toy():
def __init__(self, color, age):
self.color = color
self.age = age
action_figure = Toy('red', 10)
所做的是明确和非常简单的:
它定义了一个Toy
类。构造函数定义了两个字段,设置它们的值。最后(在main
中)创建了一个新的Toy
示例,在构造函数调用中传递字段的值。
在Java中,为了定义相同的类,我做了如下操作:
public class Toy {
private String color;
private int age;
// CONSTRUCTOR:
public Dog(String color, int age) {
this.color = color;
this.age = age;
}
}
这是相似的,但是我发现了一个很大的区别,在我的Java代码中,我在构造函数之外将类字段声明为变量,而在Python中,我直接在构造函数内部定义类字段,所以这意味着在Java中,我可以声明多个字段,然后使用构造函数方法只初始化这些字段的一个子集,例如:
public class Toy {
private String color;
private int age;
private String field3;
private String field4;
private String field5;
// CONSTRUCTOR:
public Dog(String color, int age) {
this.color = color;
this.age = age;
}
}
这里还有field3
、field4
和field5
字段,它们不会被构造函数初始化(如果我可以用setter方法设置它们的值)。
我可以在Python中做类似的事情吗?我可以在构造函数方法之外声明类字段吗?
6条答案
按热度按时间uurv41yg1#
python中的类与c++/java中的类有根本的不同,因为c++/java类有固定的数据结构和大小(字节),因为每个属性都是在所有方法之外声明或定义的(通常是私有变量),而在python中一切都是动态的(动态类型)。
在构造函数和其他方法中定义属性的选择是关于其他人能够快速理解你的代码/数据结构(尽管由于动态调用python类数据结构是不合适的)
作为动态性的一个例子,您甚至可以在运行时向类甚至示例添加新的方法和属性:
在运行时向类添加内容(这些内容将被添加到类的所有现有和将来的示例中):
在运行时向单个示例添加内容:
t2a7ltrp2#
在python中,你可以这样做:
但为了清晰起见,通常建议这样做(更多参数在这里:https://stackoverflow.com/a/38378757/4709400)来初始化构造函数中的所有示例变量,因此您可以执行以下操作:
qf9go6mv3#
在Python中并不需要这样做,在Java中你所谓的"示例变量"可以在任何需要的时候添加到类的示例中:
输出:
fnx2tebb4#
由于Python是动态类型的,你不必事先声明变量,而是在运行时初始化它们。这也意味着你不必向构造函数添加示例属性,而是可以在以后的任何时间添加它们。事实上,你可以向任何对象添加属性,包括类对象本身。向构造函数添加示例属性主要是一致性和可读性的问题。
添加到类定义中的数据属性在Python中被称为class attributes(我不懂Java,但我相信,这对应于静态变量),这对于跟踪所有类示例非常有用:
1mrurvl15#
正如您所注意到的,这不是核心python语言的一部分。
这对我来说也是一个缺失的功能,原因有几个:
*可读性/可维护性:使用在构造函数中或在别处动态定义属性的经典Python方式,当阅读代码时,对象的“契约”(或至少预期的Duck契约)是什么并不明显。
*致密性:只使用
self.<foo> = <foo>
创建长构造函数并不是最有趣的,需要的字段越多,需要编写的行就越多*扩展字段契约的能力,例如在默认值可变的情况下添加默认值工厂,或添加值验证器
*创建混合类的能力,即依赖某些字段实现某些功能的类,但不强制使用任何构造函数。
这就是我创建
pyfields
的原因,在这个库中,每个字段都被定义为一个类成员:收益率
注意,我使用的是python3.7+的类型提示语法,但是
pyfields
兼容旧版本(python 2,python3.5),参见documentation。您甚至可以通过自动创建构造函数或使用
@autofields
为您生成field()
调用来进一步简化此示例。pyfields
还提供了@autoclass
,因此您甚至可以轻松生成其他类行为,如字符串表示、等式、转换为dict等。注意,
pyfields
的灵感来自attrs
这样的巨头,但它的独特之处在于它保留了分离原则,因此它不会在背后摆弄__init__
或__setattr__
,因此允许在set上验证字段(而且不仅仅是在构造函数中),并且还开发了优雅的混合类定义字段和方法,但是没有构造函数。dtcbnfnu6#
从Python 3.7开始,
@dataclass
装饰器提供了所需的行为。甚至可以取消构造函数,并且行为是相同的。
@dataclass将启用其他函数,如示例(
__repr__
)的漂亮字符串表示:Toy(color='red', age=2, field3=None, fielg4=None, field5=None)
或者示例之间的直接比较:
全面的解释以及背后的理由可以在以下网址找到:https://realpython.com/python-data-classes/
如果希望在运行时强制执行这些类型,可以在此模型中使用
pydantic
。