本节内容:
面向对象(OO)是思想
类和对象
属性和方法
静态属性与@Property
类方法
静态方法
抽象与抽象类
接口
继承
组合
多态
封装
一、面向对象(OO)是思想
def person(name, age, sex):
#初始化
def init(name, age, sex):
person ={}
person["name"] = name
person["age"] = age
person["sex"] = sex
person["say"] = say
person["eat"] = eat
return person
def say(person):
print("My name is :%s,i'am %s old." % (person["name"], person["age"]))
def eat(person):
print("%s is eating" % person["name"])
return init(name, age, sex)
p_lw = person("老王", 42, '男')
print(p_lw["name"])
p_lw["eat"](p_lw)
#结果输出:
# 老王
# 老王 is eating
结论:OO是一种思想,class 定义类只是更加方便地实现OO思想而不是只有定义了class才是面向对象编程,同样即使定义了class也有可能不是面向对象编程
二、类和对象
前言
面向对象是一种思想、方法论,与面向过程、函数式编程一同组成了主流的三种编程思想
面向对象涵盖:面向对象分析——OOA 、面向对象设计——OOD、面向对象编程——OOP,本节重点记录Python中的OOP方法而非OO思想
类、对象的属于都是发生在特定的场景下,即不是绝对的
类与对象
类:一组事物共有的属性和方法的集合(有时也叫属性集合:数据属性,方法属性),可理解为蓝图、模板。
对象:一个个单独的事物,也叫实例
class Person:
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
def say(self):
print("My name is :%s,i'am %s old." %(self.name,self.age))
def eat(self):
print("%s is eating" %self.name)
#实例化一个Person 对象——老王
p_lw = Person("老王", 42, '男')
#调用
p_lw.eat() # 老王 is eating
初始化构造函数__init__
用于对象实例化且被隐式调用,有如下特性:
名称唯一:__init__
至少有一个参数self,表示实例本身且在对象实例化时自动传入对象名(地址)
没有返回值
self关键字用于指代当前处理的对象
class Student():
def __init__(self):
print("当前对象的地址是:%s"%self)
if __name__ == '__main__':
student1 = Student()
print(student1)
student2 = Student()
print(student2)
#结果为:
# 当前对象的地址是:<__main__.student object at>
# <__main__.student object at>
# 当前对象的地址是:<__main__.student object at>
# <__main__.student object at>
三、属性和方法
类的属性操作
#类属性和方法
class Chinese:
country = "中国"
def __init__(self,name,age):
self.name = name
self.age = age
def chadui(self):
print("%s 又他妈插队了!" %self.name)
#属性增删改查
print(Chinese.country) #中国
p1 = Chinese("王八蛋", 22)
print(p1.country) # 中国
print(p1.__dict__) # {'name': '王八蛋', 'age': 22}
#结论: .操作符 在本对象中__dict__查找,没有 找类
Chinese.fs = "黄种人"
print(Chinese.__dict__)# 新加 'fs': '黄种人'
print(p1.fs) #同样可以找到 黄种人
#修改
Chinese.country = "中华人民共和国"
print(Chinese.country, p1.country) # 中华人民共和国 中华人民共和国
def sleep(self):
print("%s 正在睡觉." %self.name) # 王八蛋 正在睡觉.
Chinese.sleep = sleep
p1.sleep()
#结论: Python对OOP 方面的语法不是严格控制,需要程序员自行控制
对象的属性操作
class Chinese:
country = "中国"
def __init__(self,name,age):
self.name = name
self.age = age
def chadui(self):
print("%s 有他妈插队了!" %self.name)
#实例化一个对象
lw = Chinese("老王", 42)
print(lw.__dict__) # {'name': '老王', 'age': 42}
lw.aihao = "抽烟"
print(lw.__dict__) # {'name': '老王', 'age': 42, 'aihao': '抽烟'}
print(Chinese.__dict__) #没有aihaoshuxing
def sahuang(self):
print("%s 就喜欢撒谎" %self.name)
lw.sahuang = sahuang
lw.sahuang(lw) # 老王 就喜欢撒谎
#上述对象函数与类无关业余其他对象无关,仅语法可以,没有实际意义
print(lw.__dict__)
print(Chinese.__dict__) # 没有撒谎函数
#拿了绿卡
lw.country = "美国" #赋值语句都是新建,对象不能修改类属性
print(lw.__dict__)
# {'name': '老王', 'age': 42, 'aihao': '抽烟', 'sahuang': , 'country': '美国'}
# 删除——遣回中国
del lw.country
print(lw.country) #中国
结论:对象的属性单独维护在对象中,独立于类的属性。对象查找属性时先从本对象__dict__中查找,没有在到类__dict__中查找
四、静态属性
这个专有名词歧义很大,首先类有自己的属性,这些属性相对于对象来说就是静态的属性。而这里的静态属性是指:在类的方法(仅有self参数)前加入装饰器@property
class Room:
style = "别墅" #类属性
def __init__(self, name, addr, length, width, height):
self.name = name
self.addr =addr
self.length = length
self.width = width
self.height = height
def get_height(self):
print("%s 房间的高度为: %s" % (self.name, self.height))
def ruzhu(self, owner):
print("%s 入住了 %s" % (owner, self.name))
@property
def area(self):
return self.length * self.width
# def cal_area(self):
# return self.length * self.width
room1 = Room("山村人家" ,"峦峰岗",100,50,20)
room1.ruzhu("张三") # 张三 入住了 山村人家
#求room1 的面积
# print(room1.cal_area()) # 5000
#方法二、使用@property 装饰器
print(room1.area) # 5000
print(room1.__dict__) #没有area 属性的
#从外部看来就是一个对象属性
结论(个人理解):Python 中 类其实可以没有属性,即:属性在有值的情况下才有意义。所以类中仅保存这类事物的共有方法即可。例如:一个人他虽然有国籍,名字,年龄,但是人这个类其实是没有这些的,Python 中把这类属性直接保存在每个对象属性中,那有时候需要对象的间接属性,如:取Room 占多少平米,通过@property 修饰符把一个方法转换成一个属性(至少表面上看起来是这样的)
五、类方法
类中的方法不与对象绑定(默认绑定即第一个参数为self),而与类自身进行绑定(self ->cls)。可以定制化调用__init__方法创建对象
class Dog:
def __int__(self,name,color):
self.name = name
self.color = color
def jiao(self):
print("%:汪汪汪!" %self.name)
@classmethod
def about_me(cls):
print("这个是一个Dog 类")
Dog.about_me()
六、静态方法
类中不与对象(self)也不与类(cls) 绑定的方法。一般是作为工具方法来使用。
import time
class TimeTest:
def __init__(self,hour,min,sec):
self.hour = hour
self.min = min
self.sec = sec
#静态方法,仅作用域限定在类中,其实就是一个独立的函数,可以有参数
@staticmethod
def show_currtime(var1,var2):
print(time.asctime())
TimeTest.show_currtime(1,2)
七、抽象与抽象类
抽象:现实生活中首先识别到一个个具体的对象,在对象的基础上提取共有属性。这个动作也叫泛化
OOA(面向对象分析)步骤:
识别一个个对象
提取对象特征,对象间的关系
抽象特征,组成类
循环第三步骤,形成类的层级关系图
抽象类
类的抽象,更抽象地描述事物
import abc
#抽象类,不能被实例化,只能被继承使用
class Shape_2D(metaclass=abc.ABCMeta):
type = "二维图形"
#初始化方法被抽象定义,so 不能实例化一个Shape_2D 对象
@abc.abstractmethod
def __init__(self):
'''
抽象方法
'''
pass
class Circle(Shape_2D):
type="圆"
def __init__(self,x,y,radius):
pass
class Line(Shape_2D):
type = "线"
def __init__(self,x1,y1,x2,y2):
pass
c1 = Circle(1,2,3)
print(c1.type) # 圆
八、接口
语法与抽象类相同,表示一组特定功能提供统一的对外功能
import abc
class I_Pay(metaclass=abc.ABCMeta):
#支付
@abc.abstractmethod
def pay(self):
pass
#退款
@abc.abstractmethod
def tuikuan(self):
pass
class AppleMobile(I_Pay):
def pay(self):
print("苹果手机支付")
def tuikuan(self):
print("苹果手机退款")
class XMMobile(I_Pay):
def pay(self):
print("小米手机支付")
def tuikuan(self):
print("小米手机退款")
抽象类 vs 接口
抽象类:还是一个类,仅对类进一步抽象;接口:特定功能的规范性描述
抽象类:描述什么是什么关系,例:战斗机、民用机是飞机,麻雀是鸟;接口:描述什么有什么功能的关系,例:飞机,鸟都有飞翔的功能
注意:在OO中所有概念(类,抽象类,接口等)都是在特定场景下,而非绝对
九、继承
即抽象的逆过程。 对象与类 '延续共有属性' 叫 : 实例化; 类与类 '延续共有属性 '叫继承,存在父-子关系。
MRO
一个元祖,记录继承顺序。
#继承树
# A
# B C
# D E
# F
class A:
def test(self):
print("A")
class B(A):
def test(self):
print("B")
class C(A):
def test(self):
print("C")
class D(B):
def test(self):
print("D")
pass
class E(C):
def test(self):
print("E")
class F(D,E):
def test(self):
print("F")
f = F()
f.test()
#新式类广度优先:左边 F->D->B->E->C->A
#老式深度优先: F->D->B->A->E->C
print(F.__mro__)
# (, , , ,
# , , )
super关键字
子类引用父类内容,使用super关键字
class Stuff:
def __init__(self,name,age,sex):
self.name = name
self.age = age
self.sex = sex
def say(self):
print("我的名字 %s" %self.name)
class Teacher(Stuff):
def __init__(self,name,age,sex,zc):
super().__init__(name,age,sex)
# super(Teacher, self).__init__(name,age,sex)
self.zc = zc
t1 = Teacher("老张",12,'男','副高')
t1.say() # 我的名字 老张
十、组合
类与类的关系除了继承,还有组合、聚合、关联
组合:一个类是其中一个类的组成部分,且紧密相关。例:Linux硬盘操作模块与LinuxOS内核,独立于Linux内核硬盘操作这个模块无法使用
聚合:一个类是一个类的一部分,但是可独立使用。例:发动机与汽车,发动机可以独立出来使用
关联:最常用,两个类都是独立的,但存在某种关系。例如:学校老师绩效考核系统中,学校是一个类,老师是一个类,但这里的老师与学校存在关联
十一、多态
功能函数参数采用父类类型定义,调用时根据传入不同的子类对象实际调用其子类自身的方法,从而同一函数看起来呈现不同的形态。没有继承就没有多态
#多态
class Animal:
def __init__(self):
pass
def move(self):
pass
class Dog(Animal):
def __init__(self):
pass
def move(self):
print("Dog is running")
class Bird(Animal):
def __init__(self):
pass
def move(self):
print("Bird is flying")
#函数让传输的动物移动两次
def run(animal):
animal.move()
animal.move()
if __name__ == "__main__":
dog1 = Dog()
bird1 = Bird()
run(dog1)
run(bird1)
#结果:
# Dog is running
# Dog is running
# Bird is flying
# Bird is flying
#好处: 调用程序仅判断一类事物,仅针对同一类型进行处理。例如:新增一个动物,本程序亦然运行无恙
十二、封装
目的
封装的目的就是隐藏复杂性。例如: 撒尿是经过一系列的动作,但是你本身是不清楚的(医生除外),掏出你的枪开干就完事了。
封装的最终结果做到内外有别,仅给出对方需要的数据,不少也不多。
方法
Python 对封装没有提供特有的关键字,仅通过变量名和约定来完成
_foo:保护的方法/属性 protect
__foo:私有方法/属性,只允许类的内部访问 private
foo:普通变量名,可以被外部访问 public
class Employee:
def __init__(self,name,age,race,salary):
self.name = name
self.age = age
self._race = race
self.__salary = salary
e = Employee('老王',55,'黑人',50000)
print(e._race) # 可以访问但不建议使用,提供给子类访问的
# print(e.__salary) # AttributeError: 'Employee' object has no attribute '_salary'
print(e._Employee__salary) # 一定要看也可以
#结论: Python 中很多功能都是不强制,讲究约定俗称