如果您来自 Java/PHP,以下是您需要了解的关于 Python 如何进行面向对象的几点。
在 Python 中,类也是完全普通的对象。这意味着类本身可以有成员变量(我们通常只称它们为属性)。您在类块中分配的所有名称都成为类本身的属性,而不是其任何实例的属性(包括def
创建方法的功能块;这就是 Python 中的所有方法)。
由于您通常有一个类和许多实例,因此类属性可以起到与 Java 类中的静态变量类似的目的,但它们的概念并不完全相同。
在类块中分配名称永远不会在类的实际实例上创建属性;在对象(任何对象、类或普通实例)上创建属性的唯一方法是在它们存在时分配它们。因此,类的实例所具有的所有变量的标准声明必须存在__init__
于 Python 中的方法中,并在self
.
例如:
class Foo(object):
foo = "I am a class attribute"
def __init__(self):
self.bar = "I am an instance attribute"
这经常使新的 Python 程序员感到困惑的是,尝试从实例中读取foo
属性实际上会起作用,并找到类属性(这就是方法调用如何找到它们应该调用的函数)。因此,在类块中声明实例属性有点像第一次随意玩耍。但实际上只有一个属性foo
,在所有实例之间共享,如果它是一个可变对象并且您希望每个实例都有自己的副本,这可能会非常糟糕。
Python 中没有访问控制,因此没有私有变量之类的东西。这是因为 Python 具有足够的动态性,您总是可以绕过任何此类限制,从而使它们变得毫无意义。
相反,Python 程序员倾向于依赖文档和约定来确保编写客户端代码的程序员知道哪些名称是公共 API 的一部分,哪些是实现细节。然后我们相信每个人都会尊重规则,而那些不必处理他们的代码的东西是他们不应该依赖改变的东西。
在任何语言中,具有微不足道的 getter 和 setter 的属性无论如何都是“公共的”。唯一可能的担忧是您可能“有一天”想要更改 getter/setter 的实现,以便在不更改接口的情况下它们不再是微不足道的。Python 可以通过属性解决这个问题(基本上允许您进行看起来像读取或写入属性的方法调用),因此绝对没有理由拥有 getter/setter 方法,除非它们实际上还有更多内容。
因此,通常如果您希望客户端代码操作对象的接口包括读取和写入字段,您只需记录一个普通属性可供读取和写入。如果您想明确一个属性(或方法)纯粹是供内部使用的,通常的约定是名称以单个下划线开头。这会强制使用该名称的任何人知道他们正在访问一个内部名称(因为他们必须键入前导下划线),因此您不必担心有人会“意外”使用私有属性。
关于构造函数,只需将__init__
方法视为__construct
来自 PHP 的方法,它就会按照您的预期执行(但请确保您记住 Python 方法显式接收self
参数;没有隐式this
)。
有这个神话在__init__
某种程度上与构造函数不同,所以你不应该称它为一个。据我所知,罪魁祸首是Dive Into Python,这很奇怪:
将其称为类的构造函数会很诱人但不正确。这很诱人,因为它看起来像构造函数(按照惯例,__init__
是为类定义的第一个方法),行为像一个构造函数(它是在新创建的类实例中执行的第一段代码),甚至听起来像一个 ( “init”当然暗示了一种构造函数的性质)。不正确,因为在调用时对象已经被构造__init__
,并且您已经拥有对类的新实例的有效引用。
但是“对象在被调用时已经被构造__init__
,并且你已经拥有对类的新实例的有效引用”准确地描述了传统构造函数的工作方式,所以我不知道作者试图在那里说明什么.
( Python 文档似乎采用的官方说法是这MyClass(arg1, arg2)
是一个构造函数表达式;__init__
并且__new__
都被描述为接收给定构造函数表达式的参数,并且__init__
被明确称为构造函数。这两个术语都很少使用在文档中;他们只是说__init__
大部分时间。)
因此,我敦促您忽略任何试图告诉您这__init__
不是构造函数的人。__init__
通过类比与构造函数来理解比试图理解它们在什么__init__
是和构造函数之间做出的关键区别会做得更好。
综上所述,PHP 类的惯用翻译如下:
class MyClass(object):
def __init__(self, myMember):
self.myMember = myMember