1.定义一个类,其中定义实例属性和成员函数,并将实例属性私有化,在类外面进行传值和获取值的操作【封装】
""" 封装 1.什么是封装:将类中的属性私有化的过程,函数和类的定义,被称为封装 2.属性私有化:对实例属性前面添加两个下划线,被私有化的属性只能在当前类中被直接访问 3.如果在外界访问私有化属性,在类中定义公开函数 4.property装饰器:将公开函数转换为属性使用 """ class Check(object): #构造函数 def __init__(self,num): self.__num = num #成员函数 def func(self): pass @property def num(self): return self.__num @num.setter def num(self,num): self.__num = num c = Check(18) #注意:此处的num并不是变量num,而是函数名 print(c.num) c.num = 20
2.分别定义一个子类和父类,在子类构造函数中调用父类构造函数【继承】
class SuperClass(object): def __init__(self,name): self.name = name class SubClass(SuperClass): def __init__(self,name,age): self.age = age #调用父类中的构造函数 #super(SubClass,self).__init__(name) #super().__init__(name) SuperClass.__init__(self,name) """ 1.什么是继承:有一个子类和父类,子类可以继承父类中所有未被私有化的属性和方法 子类 继承自 父类 / 父类 派生了 子类 2.单继承和多继承的特点 a.所有类的超类或者根类都是object b.子类对象可以直接访问父类中未被私有化的属性和方法 c.父类对象不能访问子类中特有的属性和方法 d.一个类至少有一个父类,如果不写,则默认为object 3.优缺点 """
3.分别简述封装和继承
2.知识点回顾
1.get函数和set函数:@property装饰器的工作原理
2.方法私有化:跟变量的私有化用法完全相同
3.继承树:广度解析
二、继承
1.函数重写【掌握】
override
前提:在具有继承关系的类中
作用:如果父类中的成员函数实现的功能无法满足子类的需求,则在子类中需要对父类中的成员函数进行重写【将函数重新实现一次:声明部分是一样的,实现部分重新操作】
1.1系统函数的重写
注意:并不是所有的系统函数都需要重写
__str__ __repr__
class Person(object): def __init__(self,name,age,score,height): self.name = name self.age = age self.score = score self.height = height #3.__str__的重写:重新书写函数的实现部分,返回一个当前对象的属性信息 #当重写了str的函数之后,打印对象的时候,会默认返回str函数的返回值 def __str__(self): return "name=%s age=%d score=%d height=%f" % (self.name,self.age,self.score,self.height) # def __repr__(self): # return "hello" #让repr引用指向str所指向的函数 __repr__ = __str__ p1 = Person("zhangsan",17,87,180.0) #1.如果直接打印对象,得到的结果为当前对象的地址 print(p1) #<__main__.Person object at 0x000001268D310E10> #print(p1.__str__()) print(p1.name,p1.age,p1.score,p1.height) p2 = Person("lisi",17,87,180.0) print(p2) #<__main__.Person object at 0x000001CDED7983C8> #print(p2.__str__()) print(p1.name,p1.age,p1.score,p1.height) #2.__str__;是object的函数,返回一个字符串, #__str__是有返回值的,默认情况下,该函数返回的该对象的地址字符串 #问题:当打印对象的时候,返回该对象的属性信息,则需要重写__str__函数 #4 """ 1.当str和repr未被重写的时候,使用对象,调用的是str函数,此时str返回的是对象的地址 2.当str和repr都被重写之后,使用对象,调用的是str函数,此时str返回的是属性相关的信息 3.当没有重写str,但是重写repr,使用对象,调用的是repr函数,返回的是repr指定的字符串 4.当只重写str,则调用的是str 一般写法: def __str__(self): return "xxxx" __repr__ = __str__ 以后在类中包含下面的内容: 1.构造函数[定义实例属性,并给实例属性赋值] 2.成员函数 3.重写__str__ """
1.2自定义函数的重写
注意:并不是所有的子类都需要重写父类中的函数,当父类的实现满足不了子类的需求的时候才需要重写
重写的规则:子类中出现和父类重名的函数,则子类中的函数会将父类中的覆盖掉
class Animal(object): def __init__(self,name): self.name = name def show(self): print("父类show") class Cat(Animal): def __init__(self,name): super().__init__(name) class Tiger(Animal): def __init__(self, name): super().__init__(name) class Dog(Animal): def __init__(self, name): super().__init__(name) # 2.当父类中实现的功能满足不了子类需求的时候,则需要重写 #4.重写函数的规则:只要函数名一致,则就是重写 def show(self,num1,num2): s = num1 + num2 print("子类~~~show",s) #1.当父类中的函数实现的功能满足子类需求的时候,子类对象直接调用 c = Cat("fhajg") c.show() t = Tiger("ahhf") t.show() #3.当在子类中重写了父类中的函数,则通过子类对象调用函数【就近原则】 #重写:后出现的函数将原来的函数覆盖的过程【改变一个函数变量指向的过程】 d = Dog("aaa") d.show(12,34)
三、多态
一种事物的多种体现形式,比如:动物有很多种
【定义时的类型和运行的类型不一致,也就是说,定义时不确定调用的是哪个方法,只有运行之后才能确定调用的是哪个方法】
注意:继承是多态的前提
函数的重写其实也是多态的一种体现
#父类 class Animal(object): pass #子类 class Cat(Animal): pass #定义变量 a = [] b = Animal() c = Cat() #isiantance(变量,类型):判断一个变量是否是指定的数据类型 print(isinstance(a,list)) print(isinstance(b,Animal)) print(isinstance(c,Cat)) #结论:在继承关系中,如果一个对象的数据类型是子类,则该对象的数据类型同时也是父类 print(isinstance(c,Animal)) #父类的对象不属于子类的数据类型 print(isinstance(b,Cat)) #False
案例:
""" 案例:人可以喂动物【动物有好多种】 """ from duoTai02.person import Person from duoTai02.cat import Cat from duoTai02.dog import Dog from duoTai02.tiger import Tiger p = Person() c = Cat("xiaobai") p.feedAnimal(c) d = Dog("大黄") p.feedAnimal(d)
class Person(object): # def feedCat(self,cat): # print("给食物") # cat.eat() # # def feedDog(self,dog): # print("给食物") # dog.eat() # # # def feedTiger(self,tiger): # print("给食物") # tiger.eat() #多态的体现 def feedAnimal(self,ani): print("给食物") ani.eat()
四、获取对象信息
1.type()
获取指定对象的类型
2.isinstance()
获取指定对象的类型
3.dir()
获取一个对象所有的属性和方法
import types #1.type print(type(123)) #<class "int"> #直接比较两个变量或者常量的类型是否一致 print(type(11) == type(22)) print(type("abc") == str) #可以判断函数的类型 #导入types print(type(abs) == types.BuiltinFunctionType) print(type(lambda x:x) == types.LambdaType) def fn(): pass print(type(fn) == types.FunctionType) print(type((x for x in range(10))) == types.GeneratorType) #2.isinstance() print(isinstance(11,int)) print(type(11) == int) #判断参数一的类型在参数二的元组中是否存在,存在则返回True print(isinstance([1,2,3],(dict,tuple))) print(isinstance((1,2,3),(list,tuple))) #3.dir() print(dir("abc")) #注意:类似于__xxx__的属性在Python中一般有特殊用法,自定义的标识符尽量不要使用 print(len("abc")) #等价于 print("abc".__len__()) class Text(object): def __init__(self,num): self.num = num t = Text(18) print(dir(t))
五、类中特殊的属性和方法【掌握】
1.实例属性和类属性
【面试题:简述实例【对象】属性和类属性之间的区别】
a.定义的位置不同:类属性直接定义在类中,实例属性定义在构造函数中
b.访问方式不同:实例属性 必须通过对象访问,类属性可以通过对象或者类名访问
c.在内存中出现的时机不同:类属性随着类的加载而出现,实例属性随着对象的创建而出现
d.优先级不同:如果类属性和实例属性重名的情况下,通过对象访问,则优先访问实例属性
class Person(object): #1.定义的位置不同 #类属性 #name = "abc" #age = 36 school = "千锋" def __init__(self,name): #实例属性 self.name = name # self.age = age self.score = 100 #2访问的方式不同 #类属性:对象. 类名. #实例属性:对象. print(Person.name) p1 = Person("jack") print(p1.age) print(p1.score) #print(Person.score) #报错:AttributeError: type object 'Person' has no attribute 'score' #3.优先级 del p1.name print(p1.name) """ 总结: 如果是多个对象的共享数据,则定义为类属性 如果是每个对象特有的数据,则定义为实例属性 """ per1 = Person("tom") per2 = Person("bob") print(per1.name,per2.name) #tom bob print(per1.age,per2.age) #36 #实例属性 print(id(per1.name) == id(per2.name)) #False #类似属性 print(id(per1.age) == id(per2.age)) #True """ 总结: 不同的对象访问同一个类属性,指向的是同一块内存空间 不同的对象访问同一个实例属性,指向的是不同的内存空间 """ #注意:尽量避免类属性和实例属性重名
2.动态添加属性和方法
from types import MethodType class Check(object): __slots__ = ("num1","num2","text") def func(self): print("func~~~~~") #1.动态绑定属性 c1 = Check() c1.num1 = 19 print(c1.num1) #print(c1.num2) #2.动态绑定函数 c1.func() #一个变量指向函数,该变量就可以被当做函数使用 f = c1.func print(f) f() #c1.属性 = 函数 c1.属性() # def show(self): # print("showing") # # c1.text = show # c1.text(c1) #使用系统的类MethedType创建一个函数对象 def show(self): print("showing~~~~~") #参数:需要绑定的函数的名称 绑定给哪个对象 c1.text = MethodType(show,c1) c1.text()
3.类方法和静态方法
成员方法
类方法:在一个类中,如果一个方法被@classmethod装饰器装饰
a.可以使用对象调用,也可以使用类名调用
b.类方法是属于整个类的,并不属于某个具体的对象,则在类方法中不能出现self,替代self的是一个cls的参数,代表的是当前类,在类方法中通过cls创建对象,也可以通过cls调用类中的其他的类方法
self:当前对象
cls:当前类
静态方法:在一个类中,如果一个方法被@staticmethod装饰器修饰
a.类似于类方法,可以使用对象调用,也可以使用类名调用
b.参数部分没有任何限定,不需要出现self,也不需要出现cls
class Check(object): #1.类属性 num1 = 10 def __init__(self,num2): #2.实例属性 self.num2 = num2 #3.成员函数:只能通过对象调用 def show(self): print("show:",id(self)) print("show") return 10 #4.类方法 #self和cls一样,都不是Python的关键字,只是为了约定,cls是class的缩写 @classmethod def func1(cls): print("func1:",id(cls)) print("func~~~111",cls) #<class '__main__.Check'> #cls的用法:代表就是当前类,所以可以通过cls创建对象 c2 = cls(26) #通过cls创建出来的对象可以和普通方式创建的对象一样使用,可以调用成员函数和访问变量 print(c2.show()) print(c2.num2) #5.静态方法 @staticmethod def func2(a,b): print("func~~~~22") return a + b c1 = Check(7) #c1.show() #c1.func1() #print("c1:",id(c1)) #Check.func1() print(c1.func2(2,3)) print(Check.func2(3,4)) #总结:类方法和静态方法可以不创建对象,直接通过类名调用,一般使用工具类中 #工具类:封装类,类中提供了各种功能的封装,此时如果在其他的模块中调用工具类中的函数,只需要通过类名调用
class SuperClass(object): @staticmethod def func1(): print("静态方法") @classmethod def func2(cls): print("类方法") class SubClass(SuperClass): @staticmethod def func1(): print("静态方法~~~1111") @classmethod def func2(cls): print("类方法~~~~111") s1 = SubClass() #1.子类对象可以调用父类中的静态方法和类方法 #2.在子类中也可以重写父类中的静态方法和类方法 s1.func1() s1.func2()
工具类:
class ListTool(object): #冒泡排序 @classmethod def bubbleSort(cls,newList): pass #选择排序 @classmethod def selectSort(cls, newList): pass #二分法查找 @staticmethod def searchBykey(key): pass
from listtools import ListTool list1 = [23,3,1,2,4,54,5] ListTool.bubbleSort(list1)
4.常用的系统属性
__name__:获取类名的字符串 通过类类访问,对象访问会报错 __dict__:返回一个字典 通过类名访问:获取的是该类的所有的信息,不包括对象的信息 通过对象访问:获取该对象的实例属性 __bases__:获取指定类的所有的父类【基类】 通过类名访问、
#1.__name__ if __name__ == "__main__": pass class A(object): pass class B(object): pass class Animal(A,B): num1 = 10 def __init__(self,num2): self.num2 = num2 @staticmethod def func1(): pass @classmethod def func2(cls): pass def func3(self): pass a = Animal(17) #print(a.__name__) print(Animal.__name__) #Animal #2.__dict__ print(a.__dict__) #{'num2': 17} print(Animal.__dict__) """ {'__module__': '__main__', 'num1': 10, '__init__': <function Animal.__init__ at 0x000001C980647488>, 'func1': <staticmethod object at 0x000001C980649320>, 'func2': <classmethod object at 0x000001C980649390>, 'func3': <function Animal.func3 at 0x000001C980647620>, '__dict__': <attribute '__dict__' of 'Animal' objects>, '__weakref__': <attribute '__weakref__' of 'Animal' objects>, '__doc__': None} """ #3.__bases__:得到的当前类的所有的父类,返回一个元组 print(Animal.__bases__) #(<class 'object'>,)
六、运算符重载【掌握】
重写:override
重载:overload
重写:在具有继承关系的类中,在子类中重新实现父类中的函数
重载:两个类,如果在其中一个类中重新实现了某个方法,一般是系统的方法
#+普通使用 print(30 + 26) #56 #+:拼接 print("hello" + "Python") #print("abc" + 17) #print(17 + "abc") #不同类型的数据相加得到不同的解释 class Person(object): def __init__(self,num): self.num = num def __str__(self): return "%d" % (self.num) #运算符重载 #__add__:在程序中,但凡涉及到运算,都会底层调用add函数 def __add__(self, other): return Person(self.num + other.num) p1 = Person(21) p2 = Person(7) print(type(p1)) print(type(p2)) print(p1 + p2) #28 p3 = p1.__add__(p2) print(p3.__str__()) #28 print("*********") #int + int = int str+ str = str person + person = person num1 = 20 num2 = 36 print(num1.__add__(num2)) print(num1 + num2) s1 = "hello" s2 = "Python" print(s1 + s2) print(s1.__add__(s2))