Python 对象和类
Python 是一种面向对象的编程语言。 与面向过程的程序设计(主要侧重于函数)不同,面向对象的程序设计强调对象。
对象只是数据(变量)和作用于这些数据的方法(函数)的集合。 同样,类是该对象的蓝图。
我们可以将类视为房子的草图(原型)。 它包含有关地板,门,窗户等的所有详细信息。基于这些描述,我们建造了房屋。 房子是对象。
由于可以根据房屋的蓝图建造许多房屋,因此我们可以根据类创建许多对象。 对象也称为类的实例,创建该对象的过程称为实例化。
在 Python 中定义一个类
类似的函数定义以 Python 中的def
关键字开头,类定义以class
关键字开头。
该类中的第一个字符串称为文档字符串,并具有有关该类的简短说明。 尽管不是强制性的,但强烈建议这样做。
这是一个简单的类定义。
1 2 3 |
<span class="pl-k">class</span> <span class="pl-v">MyNewClass</span>: <span class="pl-s">'''This is a docstring. I have created a new class'''</span> <span class="pl-k">pass</span> |
一个类创建一个新的本地命名空间,其中定义了其所有属性。 属性可以是数据或函数。
其中还有一些特殊属性,它们以双下划线__
开头。 例如,__doc__
给我们该类的文档字符串。
一旦定义了一个类,就会创建一个具有相同名称的新类对象。 这个类对象使我们可以访问不同的属性以及实例化该类的新对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<span class="pl-k">class</span> <span class="pl-v">Person</span>: <span class="pl-s">"This is a person class"</span> <span class="pl-s1">age</span> <span class="pl-c1">=</span> <span class="pl-c1">10</span> <span class="pl-k">def</span> <span class="pl-en">greet</span>(<span class="pl-s1">self</span>): <span class="pl-en">print</span>(<span class="pl-s">'Hello'</span>) <span class="pl-c"># Output: 10</span> <span class="pl-en">print</span>(<span class="pl-v">Person</span>.<span class="pl-s1">age</span>) <span class="pl-c"># Output: <function Person.greet></span> <span class="pl-en">print</span>(<span class="pl-v">Person</span>.<span class="pl-s1">greet</span>) <span class="pl-c"># Output: 'This is my second class'</span> <span class="pl-en">print</span>(<span class="pl-v">Person</span>.<span class="pl-s1">__doc__</span>) |
输出
1 2 3 |
<span class="pl-c1">10</span> <span class="pl-c1"><</span><span class="pl-s1">function</span> <span class="pl-v">Person</span>.<span class="pl-s1">greet</span> <span class="pl-s1">at</span> <span class="pl-c1">0x7fc78c6e8160</span><span class="pl-c1">></span> <span class="pl-v">This</span> <span class="pl-c1">is</span> <span class="pl-s1">a</span> <span class="pl-s1">person</span> <span class="pl-k">class</span> |
用 Python 创建对象
我们看到了类对象可以用来访问不同的属性。
它也可以用来创建该类的新对象实例(实例化)。 创建对象的过程类似于函数的调用。
1 |
<span class="pl-c1">>></span><span class="pl-c1">></span> <span class="pl-s1">harry</span> <span class="pl-c1">=</span> <span class="pl-v">Person</span>() |
这将创建一个名为harry
的新对象实例。 我们可以使用对象名称前缀访问对象的属性。
属性可以是数据或方法。 对象的方法是该类的相应函数。
这意味着,由于Person.greet
是函数对象(类的属性),因此Person.greet
将是方法对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<span class="pl-k">class</span> <span class="pl-v">Person</span>: <span class="pl-s">"This is a person class"</span> <span class="pl-s1">age</span> <span class="pl-c1">=</span> <span class="pl-c1">10</span> <span class="pl-k">def</span> <span class="pl-en">greet</span>(<span class="pl-s1">self</span>): <span class="pl-en">print</span>(<span class="pl-s">'Hello'</span>) <span class="pl-c"># create a new object of Person class</span> <span class="pl-s1">harry</span> <span class="pl-c1">=</span> <span class="pl-v">Person</span>() <span class="pl-c"># Output: <function Person.greet></span> <span class="pl-en">print</span>(<span class="pl-v">Person</span>.<span class="pl-s1">greet</span>) <span class="pl-c"># Output: <bound method Person.greet of <__main__.Person object>></span> <span class="pl-en">print</span>(<span class="pl-s1">harry</span>.<span class="pl-s1">greet</span>) <span class="pl-c"># Calling object's greet() method</span> <span class="pl-c"># Output: Hello</span> <span class="pl-s1">harry</span>.<span class="pl-en">greet</span>() |
输出:
1 2 3 |
<span class="pl-c1"><</span><span class="pl-s1">function</span> <span class="pl-v">Person</span>.<span class="pl-s1">greet</span> <span class="pl-s1">at</span> <span class="pl-c1">0x7fd288e4e160</span><span class="pl-c1">></span> <span class="pl-c1"><</span><span class="pl-s1">bound</span> <span class="pl-s1">method</span> <span class="pl-v">Person</span>.<span class="pl-s1">greet</span> <span class="pl-s1">of</span> <span class="pl-c1"><</span><span class="pl-s1">__main__</span>.<span class="pl-v">Person</span> <span class="pl-s1">object</span> <span class="pl-s1">at</span> <span class="pl-c1">0x7fd288e9fa30</span><span class="pl-c1">>></span> <span class="pl-v">Hello</span> |
您可能已经在类内的函数定义中注意到了self
参数,但是我们将该方法简单地称为harry.greet()
,而没有任何参数。 它仍然有效。
这是因为,只要对象调用其方法,该对象本身就会作为第一个参数传递。 因此,harry.greet()
转换为Person.greet(harry)
。
通常,调用带有 n 个参数列表的方法等同于调用带有参数列表的函数,该参数列表是通过在第一个参数之前插入方法的对象而创建的。
由于这些原因,类中函数的第一个参数必须是对象本身。 通常将其称为self
。 可以使用其他名称,但我们强烈建议您遵循约定。
现在,您必须熟悉类对象,实例对象,函数对象,方法对象及其区别。
Python 中的构造器
以双下划线__
开头的类函数由于具有特殊含义而称为特殊函数。
一个特别有趣的是__init__()
函数。 每当实例化该类的新对象时,都会调用此特殊函数。
这种类型的函数在面向对象编程(OOP)中也称为构造器。 我们通常使用它来初始化所有变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<span class="pl-k">class</span> <span class="pl-v">ComplexNumber</span>: <span class="pl-k">def</span> <span class="pl-en">__init__</span>(<span class="pl-s1">self</span>, <span class="pl-s1">r</span><span class="pl-c1">=</span><span class="pl-c1">0</span>, <span class="pl-s1">i</span><span class="pl-c1">=</span><span class="pl-c1">0</span>): <span class="pl-s1">self</span>.<span class="pl-s1">real</span> <span class="pl-c1">=</span> <span class="pl-s1">r</span> <span class="pl-s1">self</span>.<span class="pl-s1">imag</span> <span class="pl-c1">=</span> <span class="pl-s1">i</span> <span class="pl-k">def</span> <span class="pl-en">get_data</span>(<span class="pl-s1">self</span>): <span class="pl-en">print</span>(<span class="pl-s">f'<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">self</span>.<span class="pl-s1">real</span><span class="pl-kos">}</span></span>+<span class="pl-s1"><span class="pl-kos">{</span><span class="pl-s1">self</span>.<span class="pl-s1">imag</span><span class="pl-kos">}</span></span>j'</span>) <span class="pl-c"># Create a new ComplexNumber object</span> <span class="pl-s1">num1</span> <span class="pl-c1">=</span> <span class="pl-v">ComplexNumber</span>(<span class="pl-c1">2</span>, <span class="pl-c1">3</span>) <span class="pl-c"># Call get_data() method</span> <span class="pl-c"># Output: 2+3j</span> <span class="pl-s1">num1</span>.<span class="pl-en">get_data</span>() <span class="pl-c"># Create another ComplexNumber object</span> <span class="pl-c"># and create a new attribute 'attr'</span> <span class="pl-s1">num2</span> <span class="pl-c1">=</span> <span class="pl-v">ComplexNumber</span>(<span class="pl-c1">5</span>) <span class="pl-s1">num2</span>.<span class="pl-s1">attr</span> <span class="pl-c1">=</span> <span class="pl-c1">10</span> <span class="pl-c"># Output: (5, 0, 10)</span> <span class="pl-en">print</span>((<span class="pl-s1">num2</span>.<span class="pl-s1">real</span>, <span class="pl-s1">num2</span>.<span class="pl-s1">imag</span>, <span class="pl-s1">num2</span>.<span class="pl-s1">attr</span>)) <span class="pl-c"># but c1 object doesn't have attribute 'attr'</span> <span class="pl-c"># AttributeError: 'ComplexNumber' object has no attribute 'attr'</span> <span class="pl-en">print</span>(<span class="pl-s1">num1</span>.<span class="pl-s1">attr</span>) |
输出:
1 2 3 4 5 6 |
<span class="pl-c1">2</span><span class="pl-c1">+</span><span class="pl-c1">3j</span> (<span class="pl-c1">5</span>, <span class="pl-c1">0</span>, <span class="pl-c1">10</span>) <span class="pl-v">Traceback</span> (<span class="pl-s1">most</span> <span class="pl-s1">recent</span> <span class="pl-s1">call</span> <span class="pl-s1">last</span>): <span class="pl-v">File</span> <span class="pl-s">"<string>"</span>, <span class="pl-s1">line</span> <span class="pl-c1">27</span>, <span class="pl-s1">in</span> <span class="pl-c1"><</span><span class="pl-s1">module</span><span class="pl-c1">></span> <span class="pl-en">print</span>(<span class="pl-s1">num1</span>.<span class="pl-s1">attr</span>) <span class="pl-v">AttributeError</span>: <span class="pl-s">'ComplexNumber'</span> <span class="pl-s1">object</span> <span class="pl-s1">has</span> <span class="pl-s1">no</span> <span class="pl-s1">attribute</span> <span class="pl-s">'attr'</span> |
在上面的示例中,我们定义了一个新类来表示复数。 它具有两个函数,__init__()
初始化变量(默认为零),get_data()
正确显示数字。
在上面的步骤中要注意的一件有趣的事情是,可以动态创建对象的属性。 我们为对象num2
创建了一个新属性attr
,并且也读取了该属性。 但这不会为对象num1
创建该属性。
删除属性和对象
可以使用del
语句随时删除对象的任何属性。 在 Python Shell 上尝试以下操作以查看输出。
1 2 3 4 5 6 7 8 9 10 11 12 |
<span class="pl-c1">>></span><span class="pl-c1">></span> <span class="pl-s1">num1</span> <span class="pl-c1">=</span> <span class="pl-v">ComplexNumber</span>(<span class="pl-c1">2</span>,<span class="pl-c1">3</span>) <span class="pl-c1">>></span><span class="pl-c1">></span> <span class="pl-k">del</span> <span class="pl-s1">num1</span>.<span class="pl-s1">imag</span> <span class="pl-c1">>></span><span class="pl-c1">></span> <span class="pl-s1">num1</span>.<span class="pl-en">get_data</span>() <span class="pl-v">Traceback</span> (<span class="pl-s1">most</span> <span class="pl-s1">recent</span> <span class="pl-s1">call</span> <span class="pl-s1">last</span>): ... <span class="pl-v">AttributeError</span>: <span class="pl-s">'ComplexNumber'</span> <span class="pl-s1">object</span> <span class="pl-s1">has</span> <span class="pl-s1">no</span> <span class="pl-s1">attribute</span> <span class="pl-s">'imag'</span> <span class="pl-c1">>></span><span class="pl-c1">></span> <span class="pl-k">del</span> <span class="pl-v">ComplexNumber</span>.<span class="pl-s1">get_data</span> <span class="pl-c1">>></span><span class="pl-c1">></span> <span class="pl-s1">num1</span>.<span class="pl-en">get_data</span>() <span class="pl-v">Traceback</span> (<span class="pl-s1">most</span> <span class="pl-s1">recent</span> <span class="pl-s1">call</span> <span class="pl-s1">last</span>): ... <span class="pl-v">AttributeError</span>: <span class="pl-s">'ComplexNumber'</span> <span class="pl-s1">object</span> <span class="pl-s1">has</span> <span class="pl-s1">no</span> <span class="pl-s1">attribute</span> <span class="pl-s">'get_data'</span> |
我们甚至可以使用del
语句删除对象本身。
1 2 3 4 5 6 |
<span class="pl-c1">>></span><span class="pl-c1">></span> <span class="pl-s1">c1</span> <span class="pl-c1">=</span> <span class="pl-v">ComplexNumber</span>(<span class="pl-c1">1</span>,<span class="pl-c1">3</span>) <span class="pl-c1">>></span><span class="pl-c1">></span> <span class="pl-k">del</span> <span class="pl-s1">c1</span> <span class="pl-c1">>></span><span class="pl-c1">></span> <span class="pl-s1">c1</span> <span class="pl-v">Traceback</span> (<span class="pl-s1">most</span> <span class="pl-s1">recent</span> <span class="pl-s1">call</span> <span class="pl-s1">last</span>): ... <span class="pl-v">NameError</span>: <span class="pl-s1">name</span> <span class="pl-s">'c1'</span> <span class="pl-c1">is</span> <span class="pl-c1">not</span> <span class="pl-s1">defined</span> |
实际上,它比这更复杂。 当我们执行c1 = ComplexNumber(1,3)
时,会在内存中创建一个新的实例对象,并且名称c1
与之绑定。
在命令del c1
上,将删除此绑定,并从相应的名称空间中删除名称c1
。 但是,该对象继续存在于内存中,如果没有其他名称绑定,则该对象随后会自动销毁。
在 Python 中这种对未引用对象的自动销毁也称为垃圾回收。
在 Python 中删除对象会删除名称绑定