在8.2.1节说明类的基本结构时,特别强调过,若在类里面定义方法,其第一个参数必须是 self
,并且是不可或缺的(除了8.4.2和8.4.3节的之外)。当然,也可以不使用这个名称,self
只是 Python 中的惯例。但是,惯例还是要遵守,这样才能让别人也能容易阅读你的代码。
下面写一个专门研究 self
是什么的类。
>>> class P:
... def __init__(self, name):
... self.name = name
... print(self)
... print(type(self))
... print(f'id of self:{id(self)}')
...
用类 P
创建实例,会同时执行 __init__()
方法,就能看到上述 print()
函数的执行结果:
>>> a = P('Assembly')
<__main__.P object at 0x7ff5cad34be0> # (4)
<class '__main__.P'> # (5)
id of self:140693646560224 # (6)
由此结果可知:
self
引用的对象是类 P
的实例,其内存地址的十六进制形式是 0x7ff5cad34be0
;self
引用的对象类型是 P
(类也是类型);self
引用的对象的内存地址的十进制形式是 140693646560224
。>>> a
<__main__.P object at 0x7ff5cad34be0>
>>> type(a)
<class '__main__.P'>
>>> id(a)
140693646560224
>>> hex(id(a))
'0x7ff5cad34be0'
以上显示的是实例 a
的有关内容,并将十进制表示的内存地址转换为了十六进制表示。经过对比,可以下结论:self
与 a
引用了同一个实例对象——类 P
的实例。简化地说:“ self
就是实例对象。”
当创建实例的时候,实例变量作为第一个参数,被 Python 解释器传给了 self
,即8.2.2节所说过的“隐式传递”,所以初始化方法 __init__()
中的 self.name
是实例的属性。如下 a.__dict__
即查看到实例 a
的属性。
>>> a.__dict__
{'name': 'Assembly'}
重写类 P
,增加一个方法,通过此方法的调用进一步理解 self
的作用。
>>> class P:
... def __init__(self, name):
... self.name = name
... def get_name(self):
... return self.name
...
>>> a = P('BASIC')
>>> a.get_name()
'BASIC'
结合图8-3-2,理解执行 a.get_name()
时实例对象通过 self
“传递”的过程。
图8-3-2 关于 self
创建实例的时,'BASIC'
传给了参数 name
(如图示中的①)。实例对象(实例名称所引用)传给了参数 self
,如图示中的②所示。当用执行 a.get_name()
时,实例也被隐式地作为第一个参数传给该方法,如图示中的③和④所示。
总之,读者应该理解,定义类的时候,参数 self
就是预备用来实例化后引用实例的变量(或“参数”)。
下面通过一个练习,理解类在编程实践中的应用。读者或许是在网上购买的本书,通常商家会根据消费者购物的金额多少确定快递费(“包邮区”除外)。假设我们为某网上书店编写程序,要求能够根据图书的单价、消费者购买数量以及快递费,计算买家应支付总金额——为了突出体现当前已经学过的知识,此问题已被简化。
老生常谈,请读者自行尝试后再看下面的代码示例。
#coding:utf-8
'''
filename: bookshop.py
'''
class Book:
prices = {"A":45.7, "B":56.7, "C":67.8, "D":78.9, "E":90.1} # 书与单价
shipping = 5 # 快递费:5元
def __init__(self, book_name, num, free_ship):
self.book_name = book_name
self.num = num
self.free_ship = free_ship # 免快递费的阈值
# 计算总价
def totals(self):
price = Book.prices.get(self.book_name)
if price:
t = price * self.num
return (t + Book.shipping) if t < self.free_ship else t
return "There is NO this book."
if __name__ == "__main__":
book_a = Book('A', 2, 100) # 购买两本 'A' ,免快递费的阈值 100
a_total = book_a.totals()
print(a_total)
程序执行结果参考:
% python bookshop.py
96.4