python学习系列15——面向对象
(一)实例方法
(1)__init__() 为构造方法(特殊的实例方法),第一个参数必须为 self 参数。
(2)self 代表该方法的调用者,即谁在调用该方法,那么 self 就代表谁。
(3)类里面的jump()和run()方法为实例方法。
(4)在使用 Python 编程时,一般不需要使用类方法或静态方法(不具体介绍)。
代码:
1 class Dog: 2 def __init__(self): 3 print(self,"在调用构造方法") 4 # 定义一个jump()方法 5 def jump(self): 6 print(self,"正在执行jump方法") 7 # 定义一个run()方法,run()方法需要借助jump()方法 8 def run(self): 9 print(self,"正在执行run方法") 10 # 使用self参数引用调用run()方法的对象 11 self.jump() 12 dog1 = Dog() 13 dog1.run() 14 dog2 = Dog() 15 dog2.run()
结果:
<__main__.Dog object at 0x00000276B14B12B0> 在调用构造方法 <__main__.Dog object at 0x00000276B14B12B0> 正在执行run方法 <__main__.Dog object at 0x00000276B14B12B0> 正在执行jump方法 <__main__.Dog object at 0x00000276B14B1F28> 在调用构造方法 <__main__.Dog object at 0x00000276B14B1F28> 正在执行run方法 <__main__.Dog object at 0x00000276B14B1F28> 正在执行jump方法
(二)类属性和实例属性
(1)类变量
类变量推荐直接用类名访问,但也可以使用对象名访问。
代码:
1 class Address: 2 detail = '广州' 3 post_code = '510660' 4 5 def info(self): 6 # 尝试直接访问类变量 7 # print(detail) # 报错 8 # 通过类来访问类变量 9 print(Address.detail) # 输出 广州 10 print(Address.post_code) # 输出 510660 11 12 13 # 创建 2 个类对象 14 addr1 = Address() 15 addr1.info() 16 addr2 = Address() 17 addr2.info() 18 # 修改Address类的类变量 19 Address.detail = '佛山' 20 Address.post_code = '460110' 21 addr1.info() 22 addr2.info() 23 24 #动态地为类和对象添加类变量 25 Address.depict = "佛山很美" 26 print(addr1.depict) 27 print(addr2.depict)
结果:
广州 510660 广州 510660 佛山 460110 佛山 460110 佛山很美 佛山很美
(2)实例变量
实例变量只能通过对象名访问,无法通过类名直接访问。
代码:
1 class Inventory: 2 # 定义两个类变量 3 item = '鼠标' 4 quantity = 2000 5 # 定义实例方法 6 def change(self, item, quantity): 7 # 下面赋值语句不是对类变量赋值,而是定义新的实例变量 8 self.item = item 9 self.quantity = quantity 10 # 创建Inventory对象 11 iv = Inventory() 12 iv.change('显示器', 500) 13 # 访问iv的item和quantity实例变量 14 print(iv.item) # 显示器 15 print(iv.quantity) # 500 16 # 访问Inventory的item和quantity类变量 17 print(Inventory.item) # 鼠标 18 print(Inventory.quantity) # 2000
结果:
显示器 500 鼠标 2000
(三)定义属性:property()函数
(1)正常情况下的类,它包含的属性应该是隐藏的,只允许通过类提供的方法来间接实现对类属性的访问和操作。这种操作类属性的方式比较麻烦,更习惯使用“类对象.属性”这种方式。庆幸的是,Python 中提供了 property() 函数,可以实现在不破坏类封装原则的前提下,让开发者依旧使用“类对象.属性”的方式操作类中的属性。
(2)开发者调用 property() 函数时,可以传入 0 个(既不能读,也不能写的属性)、1 个(只读属性)、2 个(读写属性)、3 个(读写属性,也可删除)和 4 个(读写属性,也可删除,包含文档说明)参数。
代码:
1 class User : 2 def __init__ (self, first, last): 3 self.first = first 4 self.last = last 5 def getfullname(self): 6 return self.first + ',' + self.last 7 def setfullname(self, fullname): 8 first_last = fullname.rsplit(','); 9 self.first = first_last[0] 10 self.last = first_last[1] 11 # 使用property()函数定义fullname属性,只传入2个参数 12 # 该属性是一个读写属性,但不能删除 13 fullname = property(getfullname, setfullname) 14 u = User('悟空', '孙') 15 # 访问fullname属性 16 print(u.fullname) 17 # 对fullname属性赋值 18 u.fullname = '八戒,朱' 19 print(u.first) 20 print(u.last)
结果:
悟空,孙
八戒
朱
(四)python面向对象的三大特性
(1)封装
实际上封装有两个方面的含义:把该隐藏的隐藏起来,把该暴露的暴露出来。
python 小技巧:只要将 Python 类的成员命名为以双下画线“__”开头的,Python 就会把它们隐藏起来。
代码:
1 class User : 2 def __hide(self): 3 print('示范隐藏的hide方法') 4 def getname(self): 5 return self.__name 6 def setname(self, name): 7 if len(name) < 3 or len(name) > 8: 8 raise ValueError('用户名长度必须在3~8之间') 9 self.__name = name 10 name = property(getname, setname) 11 def setage(self, age): 12 if age < 18 or age > 70: 13 raise ValueError('用户名年龄必须在18在70之间') 14 self.__age = age 15 def getage(self): 16 return self.__age 17 age = property(getage, setage) 18 # 创建User对象 19 u = User() 20 # 对name属性赋值,实际上调用setname()方法 21 u.name = 'fk' # 引发 ValueError: 用户名长度必须在3~8之间 22 u.name = 'fkit' 23 u.age = 25 24 print(u.name) # fkit 25 print(u.age) # 25
结果:
ValueError:用户名长度必须在3-8之间
fkit
25
从该程序可以看出封装的好处,程序可以将 User 对象的实现细节隐藏起来,程序只能通过暴露出来的 setname()、setage() 方法来改变 User 对象的状态,而这两个方法可以添加自己的逻辑控制,这种控制对 User 的修改始终是安全的。
(2)继承
Python 中,实现继承的类称为子类,被继承的类称为父类(也可称为基类、超类)。
①多继承
代码:
1 class Fruit: 2 def info(self): 3 print("我是一个水果!重%g克" % self.weight) 4 class Food: 5 def taste(self): 6 print("不同食物的口感不同") 7 # 定义Apple类,继承了Fruit和Food类 8 class Apple(Fruit, Food): 9 pass 10 # 创建Apple对象 11 a = Apple() 12 a.weight = 5.6 13 # 调用Apple对象的info()方法 14 a.info() 15 # 调用Apple对象的taste()方法 16 a.taste()
结果:
我是一个水果!重5.6克
不同食物的口感不同
②父类方法重写
代码:
1 class BaseClass: 2 def foo (self): 3 print('父类中定义的foo方法') 4 class SubClass(BaseClass): 5 # 重写父类的foo方法 6 def foo (self): 7 print('子类重写父类中的foo方法') 8 def bar (self): 9 print('执行bar方法') 10 # 直接执行foo方法,将会调用子类重写之后的foo()方法 11 self.foo() 12 # 使用类名调用实例方法(未绑定方法)调用父类被重写的方法 13 BaseClass.foo(self) 14 sc = SubClass() 15 sc.bar()
结果:
③super函数——调用父类的构造方法
Python 要求,如果子类重写了父类的构造方法,那么子类的构造方法必须调用父类的构造方法。
代码:
1 # Manager继承了Employee、Customer 2 class Manager(Employee, Customer): 3 # 重写父类的构造方法 4 def __init__(self, salary, favorite, address): 5 print('--Manager的构造方法--') 6 # 通过super()函数调用父类的构造方法 7 super().__init__(salary) 8 # 与上一行代码的效果相同 9 #super(Manager, self).__init__(salary) 10 # 使用未绑定方法调用父类的构造方法 11 Customer.__init__(self, favorite, address) 12 # 创建Manager对象 13 m = Manager(25000, 'IT产品', '广州') 14 m.work() #① 15 m.info() #②
结果:
--Manager的构造方法-- 普通员工正在写代码,工资是:2500。 我是一个顾客,我的爱好是:IT产品,地址是广州
(3)多态
Canvas 的 draw_pic() 传入的参数对象只要包含 draw() 方法就行,而至于这个参数对象所属类的其他性质(例如继承自哪个类),Python 是不关心的,这就为编程增加了很大的灵活性。
代码:
1 class Canvas: 2 def draw_pic(self, shape): 3 print('--开始绘图--') 4 shape.draw(self) 5 class Rectangle: 6 def draw(self, canvas): 7 print('在%s上绘制矩形' % canvas) 8 class Triangle: 9 def draw(self, canvas): 10 print('在%s上绘制三角形' % canvas) 11 class Circle: 12 def draw(self, canvas): 13 print('在%s上绘制圆形' % canvas) 14 c = Canvas() 15 # 传入Rectangle参数,绘制矩形 16 c.draw_pic(Rectangle()) 17 # 传入Triangle参数,绘制三角形 18 c.draw_pic(Triangle()) 19 # 传入Circle参数,绘制圆形 20 c.draw_pic(Circle())

浙公网安备 33010602011771号