OOP(Object oriented Programming)面向对象编程
1 封装
根据职责将【属性】和【方法】封装到一个类中
1.1 类
1.1.1 描述
对具有相同特征(属性)或 行为(方法)的事物的统称
开发之前,需求分析阶段,需要确定程序中需要包含哪些类
类名:大驼峰命名法
属性:该类事物具有哪些特征
方法:该类事物具有哪些行为
1.1.2 定义类 class
class 类名:
def 方法1(self,参数列表):
pass
def 方法2(self,参数列表):
pass
1.1.3 私有属性和方法
对象的某些属性或方法,只希望在对象内部被使用,不对外界公开的属性和方法被称为私有属性和私有方法
子类同样不可以访问父类中的私有方法和属性
定义
__属性名 、__方法名
class Girl:
def __init__(self,name):
self.name = name
# 私有属性
self.__age = 30
# 私有方法
def __secret(self):
print("%s is %s" % (self.name, self.__age))
补充:
Python 中没有真正的私有,而是伪私有
访问私有方法:
_类名 => _类名__名称
class Girl:
def __init__(self,name):
self.name = name
# 私有属性
self.__age = 30
# 私有方法
def __secret(self):
print("%s is %s" % (self.name, self.__age))
jiejie = Girl("jiejie")
print(jiejie._Girl__age)
jiejie._Girl__secret()
1.1.4 实例属性和方法
调用方法:【实例对象.属性】、【实例对象.方法】
实例属性
__init__方法为实例属性初始化
class Cat:
def __init__(self, name)
self.name = name
# 创建对象
tom = Cat()
tom.name = "Tom"
实例方法
实例方法的第一个参数必须是self,哪个对象调用,self 就是哪个对象的引用
class Cat:
def __init__(self, name)
self.name = name
def eat(self):
print("%s eat" % self.name) # 输出 Tom eat
def run(self):
print("Cat run")
# 创建对象
tom = Cat()
tom.name = "Tom"
tom.eat()
1.1.5 类属性和方法
类是一个特殊对象,程序运行,类同样会被加载到内存中,在内存中类对象只有一份。※
调用方法:【类名.属性】、【类名.方法】
【对象名.属性】同样可以调用类属性,但是不推荐使用,容易造成混淆
类属性
给类定义的属性,用于记录这个类相关的特征,不关心具体对象的特征
例:
class Tool(object):
#类属性
count = 0
def __init__(self, name)
self.name = name
Tool.count += 1
tool1 = Tool("刀")
tool2 = Tool("枪")
补充:
属性向上查找机制
对象调用类属性时,先到该方法中查找该属性,如果没有,则会向上查找同名类属性,同样可以获取到类属性值(不推荐)
class Persion(object):
count = 0
def __init__(self):
Persion.count += 1
print("创建次数: %s " % Persion.count)
persion = Persion()
persion1 = Persion()
persion2 = Persion()
# 同样可以调用类属性(向上查找机制)
print(persion.count)
类方法
@classmethod 修饰
第一个参数必须是cls,cls. 调用类属性和方法
@classmethod
def 类方法名(cls):
pass
实例:
class Persion(object):
# 类属性
count = 0
def __init__(self):
Persion.count += 1
# 类方法
@classmethod
def clMethod(cls):
print(cls.count)
per1 = Persion()
per2 = Persion()
Persion.clMethod()
1.1.6 静态方法
调用方法:【类名.方法】
即不访问实例属性,也不访问类属性的时候,可以用到静态 ※
@staticmethod 修饰
不需要传递第一个参数
@staticmethod
def 静态方法名():
pass
当方法内部即需要访问实例属性,又需要访问类属性,应该定义为实例方法,因为类只有一个,在实例方法中通过类名.类属性 的方式也可以访问类属性。
实例:
class Dog(object):
# 静态方法
@staticmethod
def run():
print("run")
dog = Dog()
dog.run()
1.1.7 实例、静态、类类型综合练习
class Game(object):
# 类属性,历史最高分
top_score = 0
def __init__(self,name):
# 实例属性,玩家姓名
self.player_name = name
# 静态方法,显示帮助信息
@staticmethod
def show_help():
print("这是游戏帮助信息")
# 类方法,显示最高分数
@classmethod
def show_top_score(cls):
print("最高分数为%s" % cls.top_score)
# 实例方法,玩家开始游戏
def start_game(self):
print("%s 开始了游戏" % self.player_name)
# 查看帮助信息
Game.show_help()
# 查看历史最高分
Game.show_top_score()
# 创建游戏对象,并开启游戏
game = Game("chengcheng")
game.start_game()
1.1.8 object 基类
在Python3 中,object 是所有类的基类,内部封装了一些内置的属性和方法,用dir 函数可以查看
如果定义类时,没有指定父类,会默认使用object 作为该类的基类
在Python2 中定义类时,如果没有指定父类,则不会以object 作为基类
以object 为基类的类,称作新式类,新式类创建的对象,会默认拥有object 属性和内置方法
在Python2 中,如果指定了object 为基类,则该类同样属于新式类
补充:※
为了保证编写的代码能够同时在Python2 和Python3 运行,定义类时,如果没有父类,建议统一继承自object
1.2 实例对象
由类创建出的一个具体,可以直接使用
1.2.1 创建实例对象
实例类的对象创建后,在内存中就会生成一个实实在在的对象,由类创建的对象,叫做实例
对象变量 = 类名()
例:
class Cat:
def eat(self):
print("Cat eat")
def run(self):
print("Cat run")
# 创建对象
tom = Cat()
tom.eat()
tom.run()
1.2.2 给对象增加临时属性
对象变量.属性名 = “名称”
开发中不常用,并未真正修改类的属性,并且程序运行时,找不到该属性会报错
class Cat:
def eat(self):
print("Cat eat")
def run(self):
print("Cat run")
# 创建对象
tom = Cat()
tom.name = "Tom"
1.2.3 内置函数dir()
查看对象的所有属性和方法,“XX” 为该对象的内置方法和属性
方法名 | 类型 | 作用 |
---|---|---|
__new__ | 方法 | 创建对象时,会被自动调用 |
__init__ | 方法 | 对象被初始化时,会被自动调用 |
__del__ | 方法 | 对象被从内存中销毁时,会被自动调用 |
__str__ | 方法 | 返回对象的描述信息,print 函数输出使用 |
1.2.4 初始化方法init
当使用 类名() 创建对象时,会自动为对象分配内存空间,并初始化属性值
初始化方法__init__,专门定义一个类具有哪些属性
class Cat:
def __init__(self, new_name):
print("this is init")
self.name = new_name
def eat(self):
print("%s eat" % self.name)
tom = Cat("Tom")
print(tom.name) # 输出 TOM
fat = Cat("Fat")
1.2.5 销毁方法del
__del__,如果希望在对象被销毁前,再做一些事情,可以使用该方法
del 关键字,可以删除一个对象
1.2.6 生命周期
创建对象,生命周期开始调用__init__方法
__del__方法被调用后,生命周期结束
在对象的生命周期内,可以访问对象属性或调用方法
1.2.7 __str__方法
打印对象变量时,默认输出类名和该对象的内存地址,
在开发中,若希望能够打印对象的自定义内容时,可以使用该内置方法
该方法必须返回字符串
class Cat:
def __init__(self, new_name):
self.name = new_name
def __str__(self):
return "I am %s" % self.name
tom = Cat("Tom")
print(tom) #输出: I am TOM
1.2.8 None 关键字
表示一个空对象,没有任何方法和属性,可以赋值给任何一个变量
针对None 比较时,建议使用is 判断
1.2.9 身份运算符is
用于比较两个对象的内存地址是否一致,即是否是对同一个对象的引用
运算符 | 描述 | 实例 |
---|---|---|
is | 判断两个标识符是不是引用同一个对象 | x is y, 类似 id(x) == id(y) |
is not | 判断两个标识符是不是引用不同对象 | x is not y, 类似 id(x) != id(y) |
1.2.10 is 和 == 区别
is 判断两个变量引用对象(内存地址)是否是同一个
== 判断引用变量的值是否相等
2 继承
2.1 作用
可以实现代码的重用
2.2 语法
class 类名(父类名):
pass
2.3 继承的传递性
C 类继承B 类,B 类又继承A 类
则C 类具有B 类和A 类的所有属性和方法
2.4 方法的重写
当父类的方法不能满足子类需求时,可以在子类中定义一个和父类方法同名的方法并实现,达到方法的重写(override)
父类方法被重写后,可以用 “super().父类方法” 的方式来调用父类中的方法
应用技巧:
子类重写父类方法时,不必重写父类方法的所有内容,只是扩展方法功能的话,可以在重写方法中,结合super()方法来扩展子类方法。
补充:
Python 2.0 没有super() 方法,使用“父类名.方法(self)” 来实现对子类的调用,3.0之后不推荐使用。
2.5 多继承
子类可以拥有多个父类,并且具有所有父类的属性和方法
语法:
clas 子类名(父类名1, 父类名2...)
pass
注意:
如果不同父类中存在同名的方法,子类对象在调用方法时,遵循MRO 方法搜索顺序;
MRO 方法搜索顺序
针对类的内置属性__mro__ 可以查看 方法搜索顺序
class A:
def money(self):
print("money1")
class B:
def money(self):
print("money2")
class C(A,B):
pass
c = C()
c.money()
print(C.__mro__)
输出:
(<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>)
在开发中,尽量避免此种现象发生,容易产生混淆,如果父类之间存在同名的属性或者方法,应该尽量避免使用多继承。
3 多态
3.1 描述
不同的子类对象调用相同相同的父类方法,产生不同的执行结果
从而达到代码灵活度的目的
强调:多态是方法调用的技巧,不会影响到类的内部设计
3.2 前提
多态的使用前提,【继承】和 【重写】
3.2 应用 ※
父类作为参数类型,调用方法时可以传递父类也可以传递不同的子类,方法中传入不同的子类对象,会执行不同的结果。
实例:
Person 类中调用Dog 对象,而传递参数时,可以使用Dog 类的子类,从而可以灵活调用子类方法。
Person 类中game_with_dog 方法中,传入Dog 对象即可,不用关心传递的是什么Dog 对象,传入不同Dog 对象,可以实现不同效果。
class Dog(object):
def __init__(self,name):
self.name = name
def game(self):
print("%s 在地上玩耍" % self.name)
class XiaoTianDog(Dog):
def game(self):
print("%s fly game" % self.name)
class Person(object):
def __init__(self,name):
self.name = name
def game_with_dog(self,dog):
self.dog = dog
print("%s 和 %s 玩耍" % (self.name,dog.name))
dog.game()
#dog = Dog("tudou")
dog = XiaoTianDog("xiaotiangou")
xiaoming = Person("mingming")
xiaoming.game_with_dog(dog)