6.类和对象
# __init__() 构造方法(或构造函数) # 构造函数可以包含多个参数,但必须包含一个名为self的参数,且必须作为第一个参数 # 构造函数可以手动添加,如果不添加,python也会为类添加一个仅包含self参数的构造方法 class Demo: num1 = '11' num2 = '22' def __init__(self,str1): print("传入的参数:",str1) def say(self,str2): # 无论是构造方法还是实例方法,都要求将self参数作为方法的第一个参数 print(str2) # 创建对象时,会自动调用__init__()构造方法,且self参数是特殊参数,python会自动传给它值,而'12345'是传给str1的值 demo = Demo('12345') # 访问对象的变量和方法 demo.say("调用对象的方法") print(demo.num1, demo.num2) # 动态添加类对象的变量 demo.num3 = '33' print('动态添加变量', demo.num3) # 动态删除类对象的变量del demo1.num3 # 动态修改类对象的变量 demo.num1 = '44' print('动态修改变量', demo.num1) # 动态添加类对象的方法 def say1(): print('动态添加方法') demo.say1 = say1 demo.say1() # 类属性细分:类属性、实例属性、局部变量 # 类变量(或类属性):类体中,所有函数之外的变量 # 注:类变量建议用类名调用,不建议用对象名调用 # 实例变量(或实例属性):类体中,所有函数内部,以"self.变量名"方式定义的变量 class Demo1: num1 = '类变量1' def __init__(self): self.num1 = '实例变量1' def say(self): self.num2 = '实例变量2' demo1 = Demo1()# 自动调用构造函数 print('构造函数中的实例变量:',demo1.num1) demo1.say()# 需要先调用函数,才能拥有实例变量 print('实例函数中的实例变量:',demo1.num2) # 类变量和实例变量同名时,对象将无法调用类变量,它会首选实例变量 print('对象访问同名的类变量和实例变量', demo1.num1) print('类访问同名的类变量和实例变量', Demo1.num1)# 实例变量只能通过对象名访问,无法通过类名访问 # 局部变量:类体中,所有函数内部,以"变量名=变量值"方式定义的变量 # 类方法细分:实例方法、类方法、静态方法(实际几乎用不到类方法和静态方法) # 实例方法:通常情况下,在类中定义的方法默认都是实例方法,至少要包含一个self参数 # 类方法:函数前,需要用@classmethod修饰 # 第一个参数self更名为cls # 类方法建议用类名调用,不推荐用对象名调用 class Demo2: def __init__(self): self.num1 = '实例变量1' @classmethod def say(cls): print('类调用类方法,打印cls变量', cls) Demo2.say() # 静态方法:就是函数,唯一区别,静态方法的作用域为类空间,函数的作用域为程序的全局命名空间 # 函数前需要用@staticmethod修饰,没有self和cls这样的参数 class Demo3: def __init__(self): self.num1 = '实例变量1' @staticmethod def say(): print('调用静态方法') Demo3.say() # 类调用实例方法 class Demo4: def __init__(self): self.num1 = '实例变量1' def say(self): print('类调用实例方法') demo4 = Demo4() Demo4.say(demo4)# 类调用实例方法时,必须手动传入self参数(对象调用实例方法时,python会自动给self值) # python描述符,以下三个方法组成了描述符协议,如果这些方法中的任何一个被定义在一个对象中,这个对象就是一个描述符 # __get__(self,instance,owner):在读取属性时将调用该方法 # __set__(self,instance,value):在设置属性时将调用该方法 # __delete__(self,instance):对属性调用del时将调用该方法 # self:Demo5的实例对象,即myClass的属性x;instance:myClass的实例对象,即m;owner:myClass这个类,拥有所有这些东西;vlaue:对m.x的赋值,即下面例子中的20 class Demo5: def __init__(self, val = None, name = 'kbq'): self.val = val self.name = name def __get__(self, instance, owner): print("getting",self.name) return self.val def __set__(self, instance, value): print("setting",self.name) self.val = value class myClass: x = Demo5(10,'lm') # 如果一个类的某个属性有数据描述符,查找这个属性时,会调用描述符的__get__()方法;对这个熟悉赋值时,也会调用__set__()方法。 y = 5 m = myClass() print(m.x) m.x = 20 print(m.x) print(m.y) # 既保护类的封装特性,又让开发者可以使用“对象.属性”的方式操作操作类属性 # 方法一、property()函数 # 格式:属性名=property(fget=None, fset=None, fdel=None, doc=None) # 在使用property()函数时,以上4个参数可以仅指定第1个、或者前2个、或者前3个,或者全部指定。 class Demo6: def __init__(self): self.__name = "类的实例化" # name属性必须设置为私有属性,即使用__name(前面有2个下划线) def setname(self,n): self.__name = 3*n def getname(self): return self.__name def delname(self): self.__name = "xxx" aa = property(getname,setname,delname,"指明出处") demo6 = Demo6() print('实例化之后print', demo6.aa) demo6.aa = "调用setname()" print('调用setname之后print', demo6.aa) del demo6.aa # 调用delname() print('调用delname之后print', demo6.aa) # 方法二、@proprety装饰器 class Demo7: def __init__(self): self.__area = "类的实例化" @property # 将area()方法转化成同名只读属性的getter方法 def area(self): return self.__area @area.setter # 将area()方法转化成同名只读属性的setter方法 def area(self,value): self.__area = 3*value @area.deleter # 将area()方法转化成同名只读属性的deleter方法 def area(self): self.__area = "xxx" demo7 = Demo7() print('装饰器getter之后print',demo7.area) # 通过 @property装饰器,可以直接通过方法名来访问方法,不需要在方法名后添加一对“()”小括号 demo7.area = 90 print('装饰器setter之后print',demo7.area) del demo7.area print('装饰器deleter之后print',demo7.area) # 继承,如果该类没有显式指定继承自哪个类,则默认继承 object 类 # 格式:class 类名(父类1,父类2,...) class people: def say(self): print('父类people的self.name:',self.name) class animal: def display(self): print('父类animal') class Person(people,animal): # 实际使用的时候尽量不要使用多继承 pass person = Person() person.name = "输入的名字" person.say() person.display() # 父类方法重写 # 针对父类方法在子类中不适用的情况,需要对方法进行重写 class Bird: def isWing(self): print("鸟有翅膀") def fly(self): print("鸟会飞") class Ostrich(Bird): def fly(self): print("鸵鸟不会飞") ostrich = Ostrich() ostrich.fly() Bird.fly(ostrich) # 调用父类中的fly() # super()函数,(多个父类时,调用的是第一个父类的方法) # 在子类中定义新的构造方法,必须要在该方法中调用父类的构造方法(因为子类如果使用父类的方法时,涉及父类构造方法中的属性时,未调用父类构造方法相当于没有给父类构造方法中的属性做初始化,则程序运行会报错),且有以下两种方法: # 1、未绑定方法:People.__init__(self,name) # 2、super()函数:super().__init__(name) class People: def __init__(self,name): self.name = name def say(self): print("人,名字为:",self.name) class Animal: def __init__(self,food): self.food = food def display(self): print("动物,吃:",self.food) class Person(People, Animal): def __init__(self,name,food): super().__init__(name) # 调用People类的构造方法 Animal.__init__(self,food) # 使用未绑定方法调用Animal类构造方法 print('5465565454') per = Person("张三", "熟食") per.say() per.display() # __slots__属性其实是一个元组,只能限制为实例对象动态添加属性和方法,而无法限制动态地为类添加属性和方法 class Demo8: __slots__ = ('l') # 意味着该类的实例对象仅限于动态添加'l'这个熟悉或方法 def l(self): print('__slots__为类对象动态添加属性和方法l') def p(self): print('__slots__为类对象动态添加属性和方法p') demo8 = Demo8() demo8.l = l demo8.l(demo8) # demo8.p = p # 执行会报错,只能添加l # type # 用法一:type(obj)查看变量的类型 # 用法二:type(name,bases,dict)用来创建类 # name表示类的名称 # bases表示一个元组,用来存储该类的父类 # dict表示一个字典,用于表示类内定义的属性或方法 def say(self): print("type创建类") Demo9 = type('Demo9', (object,), {'say':say, 'name':"类中的变量name"}) # (object,)元组中只有一个元素时,最后的逗号不能省略 demo9 = Demo9() demo9.say() print(demo9.name) # MetaClass元类:1、必须显示继承type类;2、类中需要定义并实现__new__()方法 # 定义元类 class FirtMetaClass(type): def __new__(cls,name,bases,attrs): attrs['say_'+name] = lambda self,value,saying=name:print(saying+','+value+'!') return type.__new__(cls,name,bases,attrs) # 创建类 class Hello(object,metaclass=FirtMetaClass): pass class Hi(object,metaclass=FirtMetaClass): pass # 创建实例 hello = Hello() hi = Hi() # 调用实例 hello.say_Hello('kbq') hi.say_Hi('ora') # 多态,具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容的函数 class Animal: def talk(self): pass class Cat(Animal): def talk(self): print("喵喵喵") class Dog(Animal): def talk(self): print("汪汪汪") def func(obj): obj.talk() cat = Cat() dog = Dog() func(cat) func(dog) # 枚举类 # 枚举类不能用来实例化对象, from enum import Enum class Color(Enum): red = 1 green = 2 blue = 3 print('调用枚举类red:',Color.red) print('调用枚举类的name:',Color.green.name) print('调用枚举类的value:',Color.green.value) for color in Color: print("遍历枚举类",color) # Enum()函数可接受2个参数,第一个用于指定枚举类的类名,第二个参数用于指定枚举类中的多个成员 Num = Enum("Num",('one','two','three')) print('用Enum创建枚举类:',Num.one.name,Num.one.value)