好吧,第一件事。
Python中没有“变量声明”或“变量初始化”之类的东西。
我们简单地将其称为“分配”,但可能应该仅将其称为“命名”。
赋值的意思是“现在此名称在左侧是指评估右侧的结果,而不管其之前所指的含义(如果有的话)”。
foo = 'bar'
foo = 2 * 3
这样,Python的名称(可以说比“变量”更好的术语)没有关联的类型。值确实。您可以对任何事物重新应用相同的名称,而不管其类型如何,但是事物仍然具有取决于其类型的行为。名称只是引用值(对象)的一种方式。这回答了您的第二个问题:您不会创建变量来保存自定义类型。您不会创建变量来保存任何特定类型。您根本不需要“创建”变量。您给对象命名。
第二点:关于类,Python遵循一条非常简单的规则,实际上比Java,C ++和C#等语言更一致:块中声明的所有内容都是class
类的一部分。因此,def
这里编写的函数()是方法,即类对象的一部分(不按实例存储),就像在Java,C ++和C#中一样;但是这里的其他名字是也是该类的一部分。同样,名称只是名称,它们没有关联的类型,而函数在Python中也是对象。从而:
class Example:
data = 42
def method(self): pass
在Python中,类也是对象。
因此,现在我们创建了一个名为的对象Example
,该对象表示Example
s的所有事物的类。该对象具有两个用户提供的属性(在C ++中为“成员”;在C#中为“字段或属性或方法”;在Java中为“字段或方法”)。其中之一被命名data
,它存储整数值42
。另一个名为method
,它存储一个函数对象。(Python自动添加了更多属性。)
但是,这些属性实际上并不是对象的真正组成部分。从根本上讲,一个对象只是一堆更多的名称(属性名称),直到您了解到无法再划分的事物为止。因此,如果您有意设置值,则可以在类的不同实例之间甚至在不同类的对象之间共享值。
让我们创建一个实例:
x = Example()
现在,我们有一个名为的单独对象x
,它是的实例Example
。该data
和method
是对象不实际的一部分,但我们仍然可以找一找通过x
的,因为有些魔力,Python做幕后。method
特别是,当我们进行查询时,我们将获得一个“绑定方法”(当我们调用它时,它x
会作为self
参数,如果Example.method
直接查找则不会发生)。
当我们尝试使用时会发生什么 x.data
?
当我们检查它时,首先要在对象中查找它。如果在对象中找不到它,Python将在该类中查找。
但是,当我们分配给时 x.data
,Python将在对象上创建一个属性。它不会替换类的属性。
这使我们可以进行对象初始化。__init__
创建新实例时,Python将自动在新实例上调用类的方法(如果存在)。在这种方法中,我们可以简单地分配给属性,以在每个对象上为该属性设置初始值:
class Example:
name = "Ignored"
def __init__(self, name):
self.name = name
现在,name
当我们创建一个时必须指定一个Example
,每个实例都有自己的name
。Example.name
每当我们查找.name
实例的时,Python都会忽略class属性,因为将首先找到实例的属性。
最后一个警告:修改(变异)和赋值是不同的!
在Python中,字符串是不可变的。它们不能被修改。当您这样做时:
a = 'hi '
b = a
a += 'mom'
您无需更改原始的“ hi”字符串。在Python中这是不可能的。相反,您将创建一个新字符串'hi mom'
,并导致a
不再是的名称'hi '
,而开始是的名称'hi mom'
。我们也b
为命名'hi '
,在重新应用该a
名称后,b
它仍然是的名称'hi '
,因为它'hi '
仍然存在并且尚未更改。
但是列表可以更改:
a = [1, 2, 3]
b = a
a += [4]
现在b
也是[1、2、3、4],因为我们b
为与该名称相同的事物a
命名了一个名称,然后更改了该名称。我们没有为a
命名创建新列表,因为Python只是对列表进行了+=
不同的对待。
这对对象很重要,因为如果您有一个列表作为类属性,并使用实例来修改该列表,则在所有其他实例中都将“看到”更改。这是因为(a)数据实际上是类对象的一部分,而不是任何实例对象;(b)因为您正在修改列表而不是进行简单的分配,所以您没有创建新的实例属性来隐藏类属性。