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())

 

posted @ 2019-10-15 23:12  蜀山月  阅读(224)  评论(1)    收藏  举报