继承
在Python中展现面向对象的三大特征:【封装】【继承】【多态】 封装: 把内容封装到某个地方,便于后边的使用 条件: 把内容封装到某一个地方 从另外一个地方调用被封装的内容 对于封装来说,就是使用初始化构造方法将内容封装到对象中,然后通过对象直接或者self间接获取被封装的内容 继承: 【单继承】【多继承】 和现实生活当中的继承是一样的【子可以继承父的内容(属性和行为)】(父有的子都有,子有的父不一定有) 写法: 将多个类共有的方法提取到父类中,子类仅需继承父类而不必一一实现每个方法 这样就可以极大提高效率 减少代码的重复编写,精简代码的层级结构 便于扩展 用法: def 类名(父类1,父类2,父类3……): ''' 子类就可以继承父类中公共的属性和方法 ''' pass __mro__方法解释顺序: 功能: 查询执行顺序 描述: 方法的执行顺序可以用mro查看 一旦找到,寻找过程立即中断,便不会再继续找了
- 单继承
class Animal:
def eat(self,animal):
'''
吃
'''
print('{}吃东西'.format(animal))
pass
def drink(self,animal):
'''
喝
'''
print('{}喝水'.format(animal))
pass
pass
class dog(Animal): #继承Animal父类 此时dog就是子类
def wwj(self):
print('🐕小狗汪汪叫')
pass
pass
class cat(Animal):
def mmj(self):
print('🐱小猫喵喵叫')
pass
pass
print('---------dog---------')
d = dog()
d.eat('🐕小狗') # 具备了吃的行为 是继承了父类的行为
d.drink('🐕小狗')
d.wwj()
print('---------cat---------')
c = cat()
c.eat('🐱小猫')
c.drink('🐱小猫')
c.mmj()
- 多继承
class Shengxian:
def fly(self):
print('神仙会飞')
pass
pass
class Monkey:
def chitao(self):
print('猴子喜欢吃桃子')
pass
pass
class Sunwukong(Shengxian,Monkey):
pass
s = Sunwukong()
s.chitao()
s.fly()
问题是 当多个父类存在相同方法的时候 该调用哪一个???
class E(object):
def eat(self):
print('E.eat')
pass
pass
class D(object):
def eat(self):
print('D.eat')
pass
pass
class C(D):
def eat(self):
print('C.eat')
pass
pass
class B(D):
pass
class A(B,C):
pass
# 就近原则 广度优先
class X(D,E):
pass
class Y(E,D):
pass
class Z(B):
pass
a = A()
print(A.__mro__) # 可以显示类的依次继承关系
a.eat() # A->B->C->D 继承的顺序
x = X()
print(X.__mro__)
x.eat()
y = Y()
print(Y.__mro__)
y.eat()
z = Z()
print(Z.__mro__)
z.eat()
继承的传递
类的传递过程中,我们把父类又称为基类,子类又称为派生类 父类的属性和方法可以一级一级的传递到子类
class D(object):
def eat(self):
print('D.eat')
pass
pass
class B(D):
pass
class Z(B):
pass
z = Z()
print(Z.__mro__)
z.eat()
重写调用父类方法
重写:【方法覆盖】 子类中,有一个和父类相同名字的方法,在子类中的方法会覆盖掉父类中同名的方法 为什么重写: 父类方法不能满足子类需要,子类可以重写或者完善父类的方法
调用: 两种方法: 【手动】:父类类名.父类方法名称(self,其他参数) 【自动】:super().父类方法名称(其他参数) 自动找到父类的函数 进而调用方法 假设继承了多个父类,那么会按照顺序逐个去找,然后再调用
class Dog:
def __init__(self,name,color):
self.name = name
self.color = color
pass
def bark(self):
print('小狗类...')
pass
pass
class Keji(Dog):
def __init__(self,name,color,hight): # 属于重写父类的方法
#调用父类的方法
Dog.__init__(self,name,color) #手动调用方法
# 自动找到父类的函数 进而调用方法 假设继承了多个父类,那么会按照顺序逐个去找,然后再调用
# super().__init__(name,color) #等价与上一行
# 扩展其他属性
self.hight = hight
pass
def __str__(self):
return '{}的柯基...身高:{}cm'.format(self.color,self.hight)
def bark(self): # 属于重写父类的方法
super().bark()
print('柯基类...')
print(self.name)
pass
pass
kj = Keji('旺财','棕色',60)
kj.bark()
print(kj)
多态
多态:【定义时的类型和运行时的类型不一样】 对于不同的子类【对象】有不同的行为表现 两个前提: 【继承】多态必须发生在父类和子类之间 【重写】子类需要重写父类的方法 作用: 增加程序灵活性 增加程序扩展性
class Animal:
'''
父类【基类】动物类
'''
def say_who(self):
'''
父类方法
'''
print('我是一个动物')
pass
pass
class Duck(Animal):
'''
子类【派生类】鸭子类
'''
def say_who(self):
'''
重写父类的方法
'''
print('我是一只鸭子')
pass
pass
class Dog(Animal):
'''
子类【派生类】小狗类
'''
def say_who(self):
'''
重写父类的方法
'''
print('我是一只小狗')
pass
pass
class Cat(Animal):
'''
子类【派生类】小猫类
'''
def say_who(self):
'''
重写父类的方法
'''
print('我是一只小猫')
pass
pass
def commonInvoke(obj):
'''
统一调用方法
'''
obj.say_who()
pass
# animal= Animal()
# animal.say_who()
# duck = Duck()
# duck.say_who()
# dog = Dog()
# dog.say_who()
# cat = Cat()
# cat.say_who()
listObj = [Duck(),Dog(),Cat()]
for item in listObj:
'''
循环调用函数
'''
commonInvoke(item)
pass
类属性和实例属性
类属性:【类名.属性名】 类对象所拥有的属性,它被所有类对象的实例对象所共有,类对象和实例对象可以访问 实例属性:【实例对象.属性名】 实例对象所拥有的属性,只能通过实例对象访问
class Student:
name = 'A' #属于类属性 就是Student类对象所拥有的
def __init__(self,name,age):
self.name = name
self.age = age #实例属性
pass
pass
# a = Student(18)
# print(a.name)
# print(a.age)
b = Student('B',19)
print(b.name)
print(b.age)
print('-------通过类对象Student访问name-------')
print(Student.name)
# print('-------通过类对象Student访问 age-------')
# print(Student.age) #类对象不可以访问实例属性
class Person:
name = 'X'
def __init__(self,age):
self.age = age
pass
pass
x = Person(15)
print(x.name)
print(x.age)
#改变类属性的方法 【类名.类属性 = 新的数据】
Person.name = 'Y' #通过类对象修改类属性的值
print(x.name)
类方法和静态方法
类方法: 类对象所拥有的方法,需要用 @classmethod 来标识其为类方法 对于类方法,第一个参数必须是类对象,一般以cls作为第一个参数 类方法可以通过类对象,实例对象调用 可以对类属性进行访问、修改
静态方法: 类对象所拥有的方法,需要用 @staticmethod 来标识其为静态方法 静态方法不需要任何参数 为什么使用静态方法? 由于静态方法主要来存放逻辑性的代码,本身和类以及实例对象没有交互 也就是说,在静态方法中不会涉及到类中方法和属性操作 数据资源能够得到有效的充分利用
【类方法】【实例方法】【静态方法】对比 【类方法】 第一个参数是类对象 cls ,通过 cls 引用的类对象的属性和方法 【实例方法】 第一个参数是实例对象 self ,通过 self 引用的可能是类属性,也有可能是实例属性(具体分析) 在存在相同名称的类属性和实例属性的情况下,实例属性的优先级更高 【静态方法】 不需要额外定义参数 在静态方法中引用类属性的话,必须通过类对象来引用
class People:
country = 'China'
# 类方法 用classmethod 来进行修饰
@classmethod
def get_country(cls):
return cls.country #访问类属性
pass
@classmethod
def change_country(cls,data):
cls.country = data #修改类属性的值 在类方法中
pass
@staticmethod
def getData():
return People.country
pass
pass
print('通过类对象去引用类方法 %s'%People.get_country()) #通过类对象去引用类方法
print('通过类对象去引用静态方法 %s'%People.getData()) #通过类对象去引用静态方法
p = People()
print('通过实例对象访问类方法 %s'%p.get_country()) #通过实例对象去引用类方法
print('通过实例对象访问静态方法 %s'%p.getData()) #通过实例对象去引用静态方法
# 一般情况下,不会通过实例对象访问静态方法
print('-------Change-------')
People.change_country('英国')
print('通过类对象去引用类方法 %s'%People.get_country()) #通过类对象去引用类方法
print('通过类对象去引用类方法 %s'%People.getData) #通过类对象去引用静态方法
import time # 引入时间模块
class TimeTest:
def __init__(self,hour,min,second):
self.hour = hour
self.min = min
self.second = second
pass
@staticmethod
def showTime():
return time.strftime("%H:%M:%S",time.localtime())
pass
pass
print(TimeTest.showTime())
t = TimeTest(2,10,51)
print(t.showTime()) #没必要通过这种方式访问静态方法
私有化属性
使用私有属性的场景: 1.把特定的属性隐藏起来,不想在类的外部被使用或者直接访问 2.我想保护这个属性,不想让属性的值随意的改变 3.保护这个属性,不想让派生类【子类】去继承 为了更好的保护属性安全,即不能随意修改,将属性定义为私有属性,添加一个可调用的方法去访问 语法: 两个下划线开头,声明该属性为私有,不能在类的外部被使用或者直接访问 小结: 1.私有化的实例属性你能在外部直接访问,可以在类的内部随意的使用 2.私有化属性可以在类中修改 3.子类不能继承父类的私有化属性 【只能继承父类公共的属性和行为】
class Person:
hobby = '玩' #类属性
__sex = '🚹' #类属性
def __init__(self):
self.__name = 'A' #加两个下划线 将name属性私有化 不能在外部直接访问,在内部可以访问
self.age = 18
pass
def ChangeValue(self):
Person.__sex = '🚺'
pass
def __str__(self):
'''
私有化的属性在内部可以使用
'''
print('{}'.format(self.__sex))
return '{}'.format(self.__name)
pass
pass
class Student(Person):
def PrintInfo(self):
# print(self.__name) #【不可以!!!!!】在此访问父类中的私有属性
print(self.age)
pass
pass
p = Person()
# print(p.__name) #通过类对象 在外部访问 不能在外部直接访问
print(p.age)
print(p)
s = Student()
# print(s.__name) #
print(s)
s.PrintInfo()
print(s.hobby) #实例对象访问类属性
print(Person.hobby) #通过类名访问类属性
# print(s.__sex) #实例对象不能直接访问私有化类属性
# print(Person.__sex) #不能在类外部通过类名直接访问私有化类属性
p.ChangeValue()
print(p) #实例对象访问类属性
私有化方法
有些重要的方法,不允许外部调用,防止子类意外重写,把普通的方法设置成私有化方法 语法: 在方法名前加两个下划线 特性: 1.私有化方法一般是类内部调用,子类不能继承,外部不能调用 2.下划线说明: a.【单下划线】【_XXX】 表示 protected 类型的变量,即保护类型 只能允许其本身与子类进行访问 不能使用 from XXX import * 的方式导入 b.【双下划线】【__XXX】 表示私有类型的变量,子类不能继承 c.【头尾双下划线】【__XXX__】 魔法方法,一般是Python自有的,开发者不要创建这类的方法 d.【后单下划线】【XXX_】 避免属性名与Python关键字冲突
class Animal:
def __eat(self):
print('吃东西')
pass
def run(self):
self.__eat() #调用私有化方法
print('飞快的跑')
pass
pass
class Bird(Animal):
pass
b = Bird()
# b.eat()
b.run()
property属性
实现方式-1: 类属性:在类中定义值为property对象的类属性 实现方式-2:【推荐】 装饰器:在方法上使用装饰器
- 类属性
class Person:
def __init__(self):
self.__age = 18 # 定义一个私有化属性,属性名字前加两个下滑线【__】
pass
def get_age(self): #访问私有实例属性
return self.__age
pass
def set_age(self,age): # 修改私有属性实例
if age < 0:
print('年龄不能小于0')
pass
else:
self.__age = age
pass
pass
# 定义一个类属性 实现通过直接访问属性的形式去访问私有属性
age = property(get_age,set_age)
pass
p1 = Person()
print(p1.age)
p1.age = 20
print(p1.age)
- 装饰器
class Person:
def __init__(self):
self.__age = 18 # 定义一个私有化属性,属性名字前加两个下滑线【__】
pass
@property #使用装饰器对age进行装饰,提供一个getter方法
def age(self): #访问私有实例属性
return self.__age
pass
@age.setter #使用装饰器进行装饰,提供一个setter方法
def age(self,parms): # 修改私有属性实例
if parms < 0:
print('年龄不能小于0')
pass
else:
self.__age = parms
pass
pass
pass
p2 = Person()
print(p2.age)
p2.age = -1
print(p2.age)
p2.age = 20
print(p2.age)
__ new __方法
作用: 创建并返回一个实例对象,如果 __new__ 只调用了一次,就会得到一个对象。 继承自 object 的新式类才有 new 这一魔法方法 注意事项: 1.__new__ 是在一个对象实例化的时候调用的第一个方法 2.__new__ 至少必须要有一个参数cls,代表要实例化的类 此参数在实例化时由Python解释器自动提供,其他的参数是用来直接床底给__init__方法 3.__new__ 决定是否要使用 __init__ 方法 因为 __new__ 可以调用其他类的构造方法或者直接返回别的实例对象来作为本类的实例 如果 __new__ 没有返回实例对象,则 __init__ 不会被调用 4.在 __new__ 方法中,不能调用自己的 __new__ 方法,即:return cls.__new__(cls) 否则,会报错(RecursionError:maximum recursion depth exceeded: 超过最大递归深度) 在新式类中__new__才是真正的实例化的方法,为类提供外壳制造出实例框架 然后调用该框架内的构造方法__init__进行丰满操作 比喻建房子: __new__方法负责开发地皮,打地基 并将原材料存放在工地 __init__方法负责从工地去材料建造出地皮开发图纸规定的大楼,负责细节设置、建造、完成
class Animal:
def __init__(self) -> None:
self.color = '红色'
print('__init__被调用')
pass
# 在Python中,如果不重写 __new__ 默认结构如下:【最先被调用】
def __new__(cls,*args,**kwargs):
print('__new__被调用')
# return super().__new__(cls,*args,**kwargs) #超类返回对象
return object().__new__(cls,*args,**kwargs) #超类返回对象
pass
pass
tigger = Animal() #实例化的过程 会自动调用 __new__ 去创建实例
单例模式
概述: 常用设计模式的一种,整个系统中只有一个实例,重复打开也是使用这个实例 不管创建多少次对象,类返回的对象都是最初创建的,不会在新建其他对象 希望在系统中,某个类只能出象一个实例,单例对象就能满足要求 创建一个单例对象 基于__new__去实现【推荐的一种方式】 步骤: 【步骤一】 利用类属性,保存初次创建的实例对象 第二次实例化的时候判断类属性是否有保存实力对象 如果有,就返回类属性保存的 如果没有就调用父类__new__方法创建新的实例对象 【步骤二】 只执行一次init方法,通过类变量进行标记控制
- 单例模式1
class DataBaseClass(object):
_instance = None
def __new__(cls,*args,**kwargs):
# cls._instance = cls.__new__ 不能使用自身的new方法
# 容易造成深度递归,应该调用父类的new方法
if not hasattr(cls,'_instance'): #如果不存在,就开始创建
# if not cls._instance: #等价于上一行
cls._instance = super().__new__(cls,*args,**kwargs)
return cls._instance
pass
pass
A = DataBaseClass()
print(id(A))
B = DataBaseClass()
print(id(B))
C = DataBaseClass()
print(id(C))
- 单例模式2
class DataBaseClass(object):
_instance = None
def __new__(cls,*args,**kwargs):
# cls._instance = cls.__new__ 不能使用自身的new方法
# 容易造成深度递归,应该调用父类的new方法
if not hasattr(cls,'_instance'): #如果不存在,就开始创建
# if not cls._instance: #等价于上一行
cls._instance = super().__new__(cls,*args,**kwargs)
return cls._instance
pass
pass
class DBoptSingle(DataBaseClass):
pass
X = DBoptSingle()
print(id(X))
Y = DBoptSingle()
print(id(Y))
Z = DBoptSingle()
print(id(Z))
动态绑定属性和方法
动态语言: 在运行时可以改变其结构的语言【新的函数、对象、代码】 且已有的函数可以被删除或是其他结构上的变化 动态添加属性:运行中给对象添加属性 动态添加方法:需要使用types 给类绑定类对象和静态方法: 使用方式:类名.方法名 = XXX
import types
def DymicMethod(self):
print ("{}的性别是{},在{}学校读书".format(self.name,self.sex,Student.school))
pass
@classmethod
def classTest(cls):
print('这是一个类方法')
pass
@staticmethod
def staticMethodTest():
print('这是一个静态方法')
pass
# ----------------------------动态添加属性----------------------------
# 运行中给对象添加属性
class Student():
def __init__(self,name,age):
self.name = name
self.age = age
pass
def __str__(self):
return '姓名:{}年龄:{}'.format(self.name,self.age)
pass
pass
print('绑定类方法')
Student.TestMethod = classTest
Student.TestMethod()
print('类方法结束')
print('绑定静态方法')
Student.TestMethod = staticMethodTest
Student.TestMethod()
print('绑定静态方法结束')
a = Student('A',18)
a.TestMethod() #调用绑定类方法
b = Student('B',20)
# 给类对象添加属性
Student.school = '西安理工'
print(b.school)
# 给实例对象添加属性
a.sex = '女'
print(a)
print(b)
# 运行中给对象添加方法
# 需要导入 types
a.printInfo = types.MethodType(DymicMethod,a) #给a动态的绑定方法
a.printInfo() # 调用动态绑定的方法
b.sex = '男'
b.printInfo = types.MethodType(DymicMethod,b) #给a动态的绑定方法
b.printInfo() # 调用动态绑定的方法
__ slots __属性
概述: Python在运行时可以动态添加属性。 如果要限制在运行的时候给类添加属性, Python允许在定义 class 的时候定义一个特殊的 __slots__ 变量, 来限制该 class 实例能添加的属性。 只有在 __slots__ 变量中的属性在能被添加,没有在 __slots__ 变量中的属性会添加失败。 可以防止其他人在调用类时胡乱添加属性或方法。 __slots__ 变量子类不会继承,只在当前类中有效。 作用: 限制添加的实例属性 节约内存空间
class Student(object):
# __slots__ = ('name','age','school')
def __str__(self):
return '{} 的年龄是 {}'.format(self.name,self.age)
pass
a = Student()
a.name = 'A'
a.age = 18
# a.sex = '女' # 不能添加
a.school = '西安理工' # 可以添加
print(a)
print(a.__dict__) # 所有可以用的属性都可以在这里储存 不足的地方就是占用的内存空间大
# 可以看到在 __slots__ 变量之后,Student 类实例已经不能随意的创建不在 __slots__ 变量定义的属性
# 同时,会看到实例当中也不再有 __dict__
class subStudent(Student):
def __str__(self):
return '{} 的年龄是 {},性别为{}'.format(self.name,self.age,self.sex)
pass
b = subStudent()
b.name = 'B'
b.age = 20
b.sex = '男'
print(b)