一、上堂回顾
1.默写题目
1.定义一个类,其中定义成员变量和成员方法,并将成员变量私有化,在类外面进行传值和获取值
""" 1.封装:将属性私有化的过程【面向对象】 2.好处:提高了数据的安全性,提高了代码的复用性 3.属性私有化:被私有化的属性只能在当前类中被直接访问 4.get函数和set函数:帮助理解暴露给外界可以访问私有化属性的方法 5.@property @属性名.setter:相当于get函数和set函数的作用【将函数转换为属性进行访问】 6.函数私有化:和属性私有化类似,只能在当前类中被调用 7.变量的不同形式:xxx _xxx __xxx __xxx__ """ class Check(object): def __init__(self,name): self.__name = name def show(self): pass @property def name(self): return self.__name @name.setter def name(self,name): self.__name = name c = Check("") print(c.name) c.name = ""2.分别定义一个父类和子类,在子类中调用父类的构造函数,并创建子类对象
""" 1.继承:有一个子类和一个父类,子类可以继承父类中所有未被私有化的属性和方法 2.单继承和多继承 a.所有类的父类【超类,根类】都是object b.如果子类要继承父类中的未被私有化的成员变量,需要手动调用父类中的构造函数【三种方式】 c.子类对象可以使用父类中所有未被私有化的属性和方法,父类对象不能访问子类中特有的属性和方法 """ class SuperClass(object): def __init__(self,name): self.name = name class SubClass(SuperClass): def __init__(self,name,age): self.age = age SuperClass.__init__(self,name) super().__init__(name) super(SubClass,self).__init__(name)
2.知识点回顾
二、函数重写
前提:必须出现在继承关系的类中
本质:函数的声明部分相同,实现部分不同【覆盖父类中函数的过程】
1.系统函数的重写
__str__:在调用print打印对象的时候,会被自动调用,默认返回的对象的地址【给程序员使用的】 __repr__:在Python解释器中调用的方法【给计算机使用的】代码演示:
#系统函数的重写 class Animal(object): def __init__(self,name,age,height): self.name = name self.age = age self.height = height #__str__:获取一个对象的字符串表示形式 #注意2:如果在直接访问对象的时候,想要获取对象相关的成员变量的值,则直接重写__str__函数 def __str__(self): return "%s-%d-%f" % (self.name,self.age,self.height) def __repr__(self): return "hello" #创建对象【实例化对象】 a = Animal("大黄",10,40.0) #注意1;直接访问对象的时候,默认调用的是__str__函数,默认返回的是该对象的地址 print(a) #<__main__.Animal object at 0x00000249142FA048> ----》大黄-10-40.000000 print(a.__str__()) #<__main__.Animal object at 0x00000249142FA048>----->大黄-10-40.000000 print(a.__repr__()) print(a.name,a.age,a.height) """ 总结: 1.__str__和__repr__都未被重写的时候,直接访问对象默认调用的是__str__,但是,此时,__str__和__repr__返回的都是对象的地址 2.__str__和__repr__都被重写的时候,直接访问对象调用的是__str__,返回的是和成员变量有关的字符串 3.__repr__被重写,但是__str__未被重写,直接访问对象调用的是__repr__ 4.__str__被重写,但是__repr__未被重写,直接访问对象调用的是__str__ """ #总结;当一一个对象的属性很多的时候,并且都需要被打印,可以重写__str__,将所有成员变量的值拼接成一个字符串返回
2.自定义函数的重写
代码演示:
#自定义函数的重写:将父类中需要被重写的函数的实现部分重新实现一次 #父类 class Animal(object): def run(self): print("animal ruhning~~~") #子类 class Cat(Animal): def run(self): print("cat running~~~~~") class Dog(Animal): pass #子类中的函数将父类中的函数覆盖掉【就近原则】 c = Cat() c.run() #子类中的run d = Dog() d.run() #父类中的run #注意:重写的意义:当一个父类有多个子类的时候,如果其中的某个子类调用父类中的 函数,发现满足不了需求,则需要进行重写, # 但是,其他的子类还可以调用父类中的函数
三、多态
1.概念
一种事物的多种体现形式,举例;动物有多种
在Python中,多态指的是父类的引用指向子类的对象 ani = c c = Cat()
注意:继承是多态的前提
函数重写就是多态的体现形式
代码演示:
class Animal(object): pass class Cat(Animal): pass class Pig(Animal): pass a = list() b = Animal() c = Cat() #isinstance(变量,类型) print(isinstance(a,list)) print(isinstance(b,Animal)) #True print(isinstance(c,Cat)) #True print(isinstance(c,Animal)) #True print(isinstance(b,Pig)) #False #在继承关系中,如果一个对象的类型是某个子类,则它的数据类型也可以被看做是对应的父类,反之,则不可以
2.使用
代码演示:
#需求;饲养员喂养猫和老鼠,老虎等动物 """ 分析: 1.定义饲养员的类和动物类 2.定义老鼠类,猫类,,继承自动物类 3.在饲养员的类中定义成员函数;喂养 """ from duotai.animal import Animal from duotai.person import Person from duotai.cat import Cat from duotai.tiger import Tiger from duotai.mouse import Mouse #1.创建一个饲养员的对象 p = Person() #2.创建Cat的对象 c = Cat("小花") #3.饲养员执行自己的行为 p.feedAnimal(c) m = Mouse("aaaa") p.feedAnimal(m) #使用多态,可以简化代码,不同的子类对象可以共用一个函数,调用 class Person(object): """ def feedCat(self,cat): print("喂养" + cat.name) def feedMouse(self,mouse): print("喂养" + mouse.name) def feedTiger(self,tiger): print("喂养" + tiger.name) """ def feedAnimal(self,ani): #c = Cat() ani = c c.eat() print("喂养" + ani.name) ani.eat() class Animal(object): def __init__(self,name): self.name = name def eat(self): print("eating") from duotai.animal import Animal class Mouse(Animal): def __init__(self,name): super(Mouse,self).__init__(name)
四、获取对象信息
type():判断对象所属的数据类型 结果:<class "int">
isinstance():判断一个对象是否属于某种数据类型 结果:布尔值
dir():获取一个对象所有的属性和方法 结果:字符串列表
代码演示:
import types #1.type() print(type(123)) print(type("abc")) print(type(None)) """ <class 'int'> <class 'str'> <class 'NoneType'> """ #不同的数据类型之间可以使用==比较 print(type("123") == type(123)) #False print(type("abc") == str) #如果要判断一个对象是否是函数,需要使用types模块 def fun(): pass print(type(fun) == types.FunctionType) #是否是自定义函数 print(type(abs) == types.BuiltinFunctionType) #是否是内置函数 print(type(lambda x:x) == types.LambdaType) #是否是匿名函数 print(type((x for x in range(10))) == types.GeneratorType) #是否是生成器 #2.isinstance() print(isinstance("abc",str)) print(isinstance([1,2,3],list)) #参数1 只要是参数2中任意一种,则直接返回True【or】 print(isinstance([1,2,3],(list,tuple))) #3.dir() print(dir("abc")) #等价 #在Python中,一切皆对象 s1 = "hello" p = Peron() print(len("hello")) print("hello".__len__()) #工作原理
五、类中特殊的属性和方法【掌握】
1.实例属性和类属性
1.1实例属性和类属性的使用
实例【对象】属性和类属性之间的区别:【面试题】
a.定义的位置不同,类属性直接定义在类中,对象属性定义在构造函数中
b.访问的方式不同,类属性使用类名直接访问,对象属性使用对象访问
c.在内存中出现的时机不同,类属性随着类的加载而出现,对象属性随着对象的创建而出现
d.优先级不同,对象属性的优先级高于类属性
代码演示:
class Person(object): #1.定义位置 #类属性;直接定义在类中 name = "person" def __init__(self,name): #实例【对象】属性:定义在构造函数中 self.name = name #2.访问方式和在内存中出现的时机 print(Person.name) #类名.属性 或者 对象.属性【类属性随着类的加载而出现】 per = Person("jack") print(per.name) #对象.属性 【对象属性随着对象的创建而出现】 #3.优先级;实例属性高于类属性 print(per.name) #jack #删除对象属性,会调用和实例属性同名的类属性 del per.name print(per.name) #注意;在实际的应用中,尽量避免类属性和实例属性重名的情况
1.2动态添加实例属性和方法
__slots__:可以限制属性和方法【动态添加】代码演示:
from types import MethodType class Person(object): #限制可以动态添加的属性 __slots__ = ("name","age","func") #1.动态添加属性 per = Person() #动态添加 per.name = "jack" #2.动态添加方法 """ def show(self): print("showing") per.func = show #变量可以指向函数 #per.func() #指向函数的的变量可以被当做函数调用 per.func(per) """ #Python中提供了动态添加函数的方式:MethodType def show(self): print("showing") #参数:需要被动态添加的函数名,需要被添加的对象 per.func = MethodType(show,per) per.func() #原理:动态爱添加属性,MethodType创建了函数,类似于偏函数【MethodType在现有函数show的基础上生成了一个新的函数,赋值给了func】
2.类方法和静态方法
类方法:使用@classmethod装饰器修饰的方法被称为类方法,类方法可以使用类名调用,也可以使用对象调用,但是,一般使用类名调用类方法
特点:
a.必须有一个参数,一般写为cls【class】,代表当前类
b.类方法属于整个类,并不是属于某个对象,在类方法中禁止出现self
c.在类方法中,可以直接通过cls调用当前类的方法以及访问当前类的属性
静态方法:使用@staticmethod装饰器修饰的方法被称为静态方法,静态方法可以使用类名调用,也可以使用对象调用,但是,一般使用类名调用静态方法
代码演示:
#成员方法,类方法,静态方法 class Pig(object): #1.类属性 age = 5 def __init__(self,age): #2.实例属性 self.age = age #3.成员方法 #self代表的当前类的实例【对象】 def func1(self): print("func~~~1111") #4.类方法 #cls代表的是当前类Pig @classmethod def func2(cls): print("func~~~22222") #6.在类方法的内部通过cls创建当前类的对象,并且可以调用当前类中成员方法 p = cls(10) p.func1() print(p.age) #优先访问的是实例属性 #5.静态方法 #参数:对参数没有任何限制 @staticmethod def fun3(): print("func~~~3333") #7.调用 pig = Pig(9) #调用类方法 Pig.func2() pig.func2() #调用静态方法 Pig.fun3() pig.fun3() #调用成员方法 #注意;成员方法中必须有一个参数self,传值的时候,传的是当前对象,如果使用类名进行调用,没有对象,则self没有传值 pig.func1() #Pig.func1() #TypeError: func1() missing 1 required positional argument: 'self' print("~~~~~") #8.继承 class SubClass(Pig): def __init__(self,age): super().__init__(age) def func1(self): print("子类~~~1111") @classmethod def func2(cls): print("子类~~~~~2222") @staticmethod def fun3(): print("子类~~~333") s = SubClass(20) s.func1() s.func2() s.fun3()总结:
成员方法【实例方法】、类方法和静态方法之间的区别
a.在写法上
实例方法:第一个参数必须是实例对象,一般用self表示
静态方法:参数没有要求
类方法;第一个参数必须是当前类,一般使用cls表示
b.在继承中的相同点:子类可以继承父类中的未被私有化的实例方法,静态方法,类方法,如果三种方法都被重写,仍然优先调用子类中的方法
3.类常用属性
__name__: 通过类名访问 获取类名相关字符串 注意:使用对象访问报错 __dict__: 通过类名访问,获取该类的信息,包含类方法,类属性,静态方法,成员方法,返回的是一个字典 通过对象访问:获取的是该对象的信息,包含该对象的所有属性和值,返回是一个字典 __bases__: 通过类名访问 查看当前类的所有的父类【基类】代码演示:
class Animal(object): pass class Tiger(Animal): age = 10 name = "abc" def haha(self): print("haha") @classmethod def show(cls): print("show") @staticmethod def check(): print("check") print(Tiger.__name__) t = Tiger() #print(t.__name__) print(Tiger.__dict__) print(t.__dict__) print(Tiger.__base__) #<class '__main__.Animal'> #使用频率最广是__name__,if __name__ == "__main__"
六、运算符重载
重写:override 重载:overload
运算符的重载:和重写类似,对类的专有方法进行重载【将系统函数的实现部分重新实现】
和重写的区别:重写必须出现在具有继承关系的类中,重载没有要求
代码演示:
#__add__() #使用+进行运算,在计算机底层实质上相当于对象调用了__add__,num1 + num2---->num1.__add__(num2) print(1 + 2) #3 print("1" + "2") #12 print("1".__add__("2")) #print("1" + 1) #不同的类型会有不同的解释 #思考问题:如果两个对象相加会怎样? class Person(object): def __init__(self,num): self.num = num #方法重写__str__ def __str__(self): return "num = %d" %(self.num) #方法重载 def __add__(self, other): #两个Person类型的数据相加,得到的结果应该也是Person类型 return Person(self.num + other.num) p1 = Person(10) p2 = Person(20) print(p1,p2) print(p1 + p2) #30 #p1 + p2 --->p1.__add__(p2) print(p1.__add__(p2))
七、单例设计模式【掌握】
1.概念
存在的问题:如果在指定情况下,如果需要使用的对象是同一个,就没有必要浪费内存空间创建不同的对象
解决办法:单例设计模式
什么是设计模式?
已经存在的可以解决特定问题的方案【模板】
23种,其中比较常用的单例设计模式,工厂设计模式,代理委托设计模式,装饰者设计模式
什么是单例设计模式?
程序在运行的过程中,确保某一个类只有一个实例【对象】,不管在哪个模块中获取该实例,获取到的都是同一个实例
单例设计模式的核心:一个类有且只有一个实例,并且这个实例需要应用在程序的各个位置
2.使用
2.1使用模块
Python中的模块本身就是一个单例
模块的工作原理:当模块被第一次导入的时候,会生成一个.pyc文件,当第二次导入的时候,会直接加载.pyc文件,跟原来的py文件没有关系
2.2使用new
__new__();从无到有的过程 为了保证一个类只有一个实例,使用__new__来控制实例的创建过程代码演示:
class Singleton(object): #1.声明一个类属性:可以使用类名访问 instance = None #2.重写new #new函数是一个类函数 def __new__(cls, *args, **kwargs): #3.如果instance为None,则创建对象,并给instance赋值;如果instance不为None,则直接将instance返回 #cls.instance if cls.instance == None: cls.instance = super(Singleton,cls).__new__(cls,*args, **kwargs) print("~~~~~~") return cls.instance #super(Singleton,self).__init__() class MyClass(Singleton): pass #注意:__new__类似于构造函数,是在创建对象的时候调用 one = MyClass() #只有第一次调用的时候在创建对象 two = MyClass() #在获取对象 print(id(one) == id(two)) #True
2.3装饰器
代码演示:
#装饰器;将其应用于一个类上 #将装饰器应用于一个函数上,则外部函数需要传一个函数,应用到一个类上,则需要传一个类,使用cls代表类 def singleton(cls): #cls = Test #变量 instance = {} #成员方法 def getSingleton(*args, **kwargs): #思路:如果cls在字典中,则直接将结果【对象,实例】返回,如果cls不存在,则cls作为key,实例作为value存储到字典中 if cls not in instance: instance[cls] = cls(*args, **kwargs) #{Test:Test()} return instance[cls] return getSingleton @singleton class Test(object): pass """ f = singleton(Test) result = f() """ t1 = Test() t2 = Test() print(id(t1) == id(t2))
2.4使用在类中
代码演示: