super,封装,类中的三个装饰器,反射
程序员文章站
2022-03-18 16:05:26
1.super 【1】在py3中怎么用?在py2(新式类/经典类)中怎么用? 【2】在单继承中执行父类的同名方法的时候怎么用? 【3】super方法和mro方法的关系是什么? 2.封装: 3.类中的三个装饰器(内置函数) 1.property 2.classmethod 3. ......
1.super
【1】在py3中怎么用?在py2(新式类/经典类)中怎么用?
-super是按照mro顺序来寻找当前类的下一个类。 -在py3中不需要传参数,自动就帮我们寻找当前类的mro顺序的下一个类中的同名方法。super后()内默认传当前类的类名,当前类的对象。例:super(d,self).func() -在py2中的新式类中,需要我们主动传递参数super(子类的名字,子类的对象).函数名() ,这样才能够帮我们调用到这个子类的mro顺序的下一个类中的方法。 -在py2的经典类中,并不支持使用super来找下一个类。 #在多继承中遵循mro顺序: class a(object): def func(self): print('a') class b(a): def func(self): super().func() print('b') class c(a): def func(self): super().func() print('c') class d(b,c): def func(self): super().func() #或: # super(d,self).func() print('d') # 在d类中找super的func,那么可以这样写 super().func() # 也可以这样写 super(d,self).func() (并且在py2的新式类中必须这样写) d().func() #super是按照mro顺序来寻找当前类的下一个类,查找顺序是dbca,找到a后开始执行a,执行完a再执行c,执行完c再执行b,执行完b再执行a。所以打印顺序与查找顺序相反 # a # c # b # d
【2】在单继承中执行父类的同名方法的时候怎么用?
# 在单继承的程序中,super就是找父类,用来调用父类的同名方法时用 ,例如 super().__init__(name) # 当方法名还有除了self以外的参数时,还要添加上这些参数。比如:name # 用super必须是在继承的前提下,class vipuser 继承 class user # 在单继承中 mro 就是一个一个父类的顺序。 【记】场景:在单继承的程序中调用父类的同名方法时 class user: def __init__(self,name): self.name = name class vipuser(user): def __init__(self,name,level,strat_date,end_date): # user.__init__(self,name) # 主动传self和name,此方式可以不是继承,class vipuser不继承 class user super().__init__(name) # 推荐的方式(源码中有单继承时多用这种方式),super后()内默认传当前类的类名,当前类的对象,只传 name 即可 # super(vipuser,self).__init__(name) self.level = level self.strat_date = strat_date self.end_date = end_date 太白 = vipuser('太白',6,'2019-01-01','2020-01-01') print(太白.__dict__)#{'name': '太白', 'level': 6, 'strat_date': '2019-01-01', 'end_date': '2020-01-01'}
【3】super方法和mro方法的关系是什么?
2.封装:
1.概念: 【1】 封装: 就是把属性或者方法装起来。 【2】广义上的封装: 把属性和方法装起来,外面不能直接调用了,要通过类的名字来调用。 【3】狭义上的封装: 把属性和方法藏起来,外面不能调用,只能在内部偷偷调用。 2.使用私有的三种情况:私有的前提下是在类的内部 (1)给一个名字前面加上了双下划线的时候,这个名字就变成了一个私有的。 (2)所有的私有的内容或者名字都不能在类的外部调用,只能在类的内部使用了 (3)所有的私有化都是为了让用户不在外部调用类中的某个名字 (4)如果完成私有化 那么这个类的封装度就更高了。封装度越高,各种属性和方法的安全性也越高 但是代码越复杂。 【1】不想让你看,也不想让你改。 class user: def __init__(self,name,passwd): self.usr = name self.__pwd = passwd # 私有的实例变量/私有的对象属性 alex = user('alex','sbsbsb') print(alex.__pwd) # 报错 attributeerror: 'user' object has no attribute '__pwd' # print(alex.pwd) # 报错 attributeerror: 'user' object has no attribute 'pwd' 【2】可以让你看, 但不让你改。 class user: def __init__(self,name,passwd): self.usr = name self.__pwd = passwd # 私有的实例变量/私有的对象属性 def get_pwd(self): # 表示的是用户不能改,只能看 通过 私有 + 某个get方法实现的 return self.__pwd alex = user('alex','sbsbsb') print(alex.get_pwd())#sbsbsb 【3】可以看也可以改, 但是要求你按照我的规则改。 class user: def __init__(self,name,passwd): self.usr = name self.__pwd = passwd # 私有的实例变量/私有的对象属性 def get_pwd(self): # 表示的是用户不能改,只能看 通过 私有 + 某个get方法实现的 return self.__pwd def change_pwd(self): # 表示用户必须调用我们自定义的修改方式来进行变量的修改 私有 + change方法实现 pass alex = user('alex','sbsbsb') print(alex.get_pwd())#sbsbsb 3.封装的语法 【1】私有的静态变量 class user: __country = 'china' # 私有的静态变量 def func(self): print(user.__country) # 在类的内部可以调用 # print(user.country) # 报错 在类的外部不能调用 # print(user.__country)# 报错 在类的外部不能调用 user().func()#china 【2】私有的实例变量 class user: def __init__(self,name,passwd): self.usr = name self.__pwd = passwd # 私有的实例变量/私有的对象属性 def get_pwd(self): return self.__pwd alex = user('alex','sbsbsb') print(alex.get_pwd())#sbsbsb 【3】私有的绑定方法 import hashlib class user: def __init__(self,name,passwd): self.usr = name self.__pwd = passwd # 私有的实例变量 def __get_md5(self): # 私有的绑定方法 md5 = hashlib.md5(self.usr.encode('utf-8')) md5.update(self.__pwd.encode('utf-8')) return md5.hexdigest() def getpwd(self): return self.__get_md5() alex = user('alex','sbsbsb') print(alex.getpwd())#d6170374823ac53f99e7647bab677b92 4.私有的特点: 能不能在类的内部使用?-能 能不能在类的外部使用?-在外部不能直接调用 能不能在类的子类中使用?-不能 #面试题1: class foo(object): def __init__(self): self.__func() def __func(self): #_foo__func print('in foo') class son(foo): def __func(self): #_son__func print('in son') son()#in foo #2: class foo(object): def __func(self): print('in foo') class son(foo): def __init__(self): self.__func() son()#报错:attributeerror: 'son' object has no attribute '_son__func' #3: class foo(object): def __init__(self): self.func() def func(self): print('in foo') class son(foo): def func(self): print('in son') son()#in son 5.原理: 如何变形?【记】在类的内部使用的时候,自动的把当前这句话所在的类的名字拼在私有变量前完成变形。 在哪里定义的时候变形? ---> 所有加双下划线的定义只有在类的内部定义的时候才会变成私有的, 在类的外部根本不能定义私有的概念。 # 加了双下划线的名字为啥不能从类的外部调用了? class user: __country = 'china' # 私有的静态变量 __role = '法师' # 私有的静态变量 def func(self): print(self.__country) # 在类的内部使用的时候,自动的把当前这句话所在的类的名字拼在私有变量前完成变形 print(user._user__country)#china print(user._user__role)#法师 # __country -->'_user__country': 'china' # __role -->'_user__role': '法师' # user.__aaa = 'bbb' # 在类的外部根本不能定义私有的概念,不会变成私有的。 print(user.__dict__) #{'__module__': '__main__', '_user__country': 'china', '_user__role': '法师', 'func': <function user.func at 0x0000021fbe609c80>, '__dict__': <attribute '__dict__' of 'user' objects>, '__weakref__': <attribute '__weakref__' of 'user' objects>, '__doc__': none} 6.类中变量的级别,哪些是python支持的,哪些是python不支持的 # 在其他语言中的数据的级别都有哪些?在python中有哪些? 【1】public 公有的 类内类外都能用,父类子类都能用 python支持 【2】protect 保护的 类内能用,父类子类都能用,类外不能用 python不支持 【3】private 私有的 本类的类内部能用,其他地方都不能用 python支持
3.类中的三个装饰器(内置函数)
1.property
1. @property : 【1】把一个方法伪装成一个属性,在调用这个方法的时候不需要加()就可以直接得到返回值 【2】property装饰的这个方法 是 不能有参数的。 变量的属性和方法? 属性 :圆形的半径\圆形的面积\name\age 方法 :登录 注册 #1: from math import pi class circle: def __init__(self,r): self.r = r @property # 把一个方法伪装成一个属性,在调用这个方法的时候不需要加()就可以直接得到返回值 def area(self): return pi * self.r**2 c1 = circle(5) print(c1.r)#5 print(c1.area)#78.53981633974483 #2: import time class person: def __init__(self,name,birth): self.name = name self.birth = birth @property def age(self): # 装饰的这个方法 不能有参数 return time.localtime().tm_year - self.birth #通过计算才能拿到某个结果,这个结果又直接是一个属性值,把一个方法伪装成属性 太白 = person('太白',1998) print(太白.age)#21 #3: property的第二个应用场景 : 和私有的属性合作的,把相关的方法伪装成私有属性 class user: def __init__(self,usr,pwd): self.usr = usr self.__pwd = pwd #定义成私有 @property def pwd(self): return self.__pwd #可以看,但不能改 alex = user('alex','sbsbsb') print(alex.pwd)#sbsbsb #4: #商品打折的应用场景 class goods: discount = 0.8 def __init__(self,name,origin_price): self.name = name self.__price = origin_price #私有化 @property def price(self): return self.__price * self.discount apple = goods('apple',5) print(apple.price)#4.0 #5:property进阶1(了解) class goods: discount = 0.8 def __init__(self,name,origin_price): self.name = name self.__price = origin_price @property def price(self): return self.__price * self.discount @price.setter #@ 方法名.setter 三个地方的名字一致,比如都是price def price(self,new_value): #定义一个和上面的property装饰的方法同名的方法,可以比上面多接收一个参数 if isinstance(new_value,int): #只有new_value是int类型才允许修改 self.__price = new_value #修改 apple = goods('apple',5) print(apple.price) # 4.0 # 对象.名字 -->调用的是被 @property 装饰的price apple.price = 10 # 对象.名字 = xx -->调用的是被 setter 装饰的price 重新赋值 print(apple.price) # 8.0 如果没有apple.price = 10 ,此处结果还是4.0 #6:property进阶2(了解) class goods: discount = 0.8 def __init__(self,name,origin_price): self.name = name self.__price = origin_price @property def price(self): return self.__price * self.discount @price.setter def price(self,new_value): if isinstance(new_value,int): self.__price = new_value @price.deleter def price(self): #5 个price必须是同样的名字 del self.__price #删除私有属性 apple = goods('apple',5) print(apple.price)#4.0 #调用property装饰的方法 apple.price = 'ashkaksk' #不能修改 print(apple.price)#4.0 #调用setter装饰的方法 del apple.price # 调用deleteer装饰的方法。del apple.price 并不能真的删除什么,只是调用对应的被@price.deleter装饰的方法del self.__price而已 print(apple.price)#attributeerror: 'goods' object has no attribute '_goods__price'
2.classmethod
【1】引入: #修改折扣,所有商品都修改。 class goods: __discount = 0.8 def __init__(self): self.__price = 5 self.price = self.__price * self.__discount def change_discounnt(self,new_discount): #传入了self 和 new_discount两个参数,但是self没有用上。一般在函数中不要传入一个用不到的参数。 goods.__discount = new_discount #有一个change_discounnt方法默认传self参数,但在本函数中用不到该参数 apple = goods() print(apple.price) # 4 apple.change_discounnt(0.5) #不太符合逻辑,所有商品折扣的修改不应该依赖于某一个对象。此处为了调用到修改折扣的方法还必须先实例化一个对象 apple2 = goods() print(apple2.price)#2.5 【2】classmethod @classmethod 把一个对象绑定的方法 修改成一个 类方法,被装饰的方法会成为一个类方法。 第一,在方法中仍然可以引用 类中的 静态变量 第二,可以不用实例化对象,就直接用类名在外部调用这个方法 什么时候用@classmethod? 1.定义了一个方法,默认传self,但这个self没被使用 2.并且你在这个方法里用到了当前的类名, 或者你准备使用这个类的内存空间中的名字的时候 class goods: #cls指向当前所在类的名字,在类内部统一使用cls的话,修改类名时只需修改class后的类名,内部不用修改,非常方便 __discount = 0.8 def __init__(self): self.__price = 5 self.price = self.__price * self.__discount @classmethod ## 把一个对象绑定的方法 修改成一个 类方法 ,self 改为 cls,cls指向当前所在类的名字,而不是指向对象的名字了。 def change_discounnt(cls,new_discount): # print(cls,goods)#<class '__main__.goods'> <class '__main__.goods'> cls.__discount = new_discount #可以不用实例化对象,(在实例化之前)就能直接用类名在外部调用这个方法 goods.change_discounnt(0.6) # 类方法可以通过类名调用。不用对象就能调用这个方法了。相当于把goods也当做一个参数,传g给了cls。change_discounnt(cls,new_discount) apple = goods() print(apple.price)#3 apple.change_discounnt(0.5) # 类方法可以通过对象名调用 apple2 = goods() print(apple2.price)#2.5 #例:在自己类里做了自己类的实例化 import time class date: def __init__(self,year,month,day): self.year = year self.month = month self.day = day @classmethod def today(cls): #类方法-->传cls struct_t = time.localtime() date = cls(struct_t.tm_year,struct_t.tm_mon,struct_t.tm_mday) #在自己类里做了自己类的实例化,做了开辟对象空间,调用__init__等操作,把self作为返回值传给date return date #将对象返回回去 date对象 = date.today() print(date对象.year)#2019 print(date对象.month)#6 print(date对象.day)#5
3.staticmethod
# @staticmethod 被装饰的方法会成为一个静态方法 本身是一个普通的函数,被挪到类的内部执行,那么直接给这个函数添加@staticmethod装饰器就可以了, 在函数的内部既不会用到self变量,也不会用到cls类。 class user: pass @staticmethod def login(a,b): # 本身是一个普通的函数,被挪到类的内部执行,那么直接给这个函数添加@staticmethod装饰器就可以了 print('登录的逻辑',a,b) # 在函数的内部既不会用到self变量,也不会用到cls类 obj = user() user.login(1,2)#登录的逻辑 1 2 obj.login(3,4) #登录的逻辑 3 4 总结: class a: country = '中国' #静态变量 def func(self): #绑定方法 print(self.__dict__) @classmethod def clas_func(cls): #类方法 print(cls) @staticmethod def stat_func(): #静态方法 print('普通函数') @property def name(self): #是个伪装成属性的方法 return 'wahaha' 能定义到类中的内容: 【1】 静态变量 是个所有的对象共享的变量 由对象\类调用 但是使用对象不能对其重新赋值 【2】 绑定方法 是个自带self参数的函数 由对象调用 【3】 类方法 是个自带cls参数的函数 由对象\类调用 【4】 静态方法 是个啥都不带的普通函数 由对象\类调用 【5】 property属性 是个伪装成属性的方法 由对象调用 但不加括号
4.反射
反射:用字符串数据类型的名字 来操作这个名字对应的函数\实例变量\绑定方法\各种方法
1.引入: #用户输入一个名字,你来打印这个变量的值 name = 'alex' age = 123 n = input('>>>') if n == 'name':print(name) elif n == 'age':print(age) 2.有些时候 你明明知道一个变量的字符串数据类型的名字,你想直接调用它,但是调不到-->使用反射 【1】反射对象的 实例变量(属性) 【2】反射类的 静态变量/绑定方法/其他方法 【3】模块中的 所有变量 a.被导入的模块 b.当前执行的py文件 - 脚本 hasattr getattr: 字符串数据类型的变量名,采用getattr(对象,'变量名')获取变量的值。python中一切皆对象,此处对象可以是对象名,类名,模块名,本文件名 callable:判断一个变量是不是可调用的,判断这个变量后面能不能加括号 #1: 对象名.属性名 ==> getattr(对象名,'属性名') class person: def __init__(self,name,age): self.name = name self.age = age def qqxing(self): print('qqxing') alex = person('alex',83) wusir = person('wusir',74) ret = getattr(alex,'name')#反射对象的实例变量 print(ret)#alex ret = getattr(wusir,'name') print(ret)#wusir ret = getattr(wusir,'qqxing')#反射类的绑定方法 ret()#qqxing 函数地址 + () 函数的调用 #2: #a.py模块 class wechat:pass class alipay:pass def sww(): print('爽歪歪') lst = [1,2,3,4,5] dic = {'k':'v'} we = wechat() #当前脚本:反射被导入模块中的 所有变量 a.alipay ==> getattr(a,'alipay') ##1: import a print(a.wechat)#<class 'a.wechat'> print(a.alipay)#<class 'a.alipay'> # 对象名.属性名 ==> getattr(对象名,'属性名') # a.alipay ==> getattr(a,'alipay') print(getattr(a, 'alipay'))#<class 'a.alipay'> print(getattr(a, 'wechat'))#<class 'a.wechat'> ##2: import a import sys print(sys.modules) print(sys.modules['a'].alipay)#<class 'a.alipay'> print(a.alipay)#<class 'a.alipay'> print(getattr(a,'alipay'))#<class 'a.alipay'> print(getattr(sys.modules['a'],'alipay'))#<class 'a.alipay'> #3:反射当前脚本中 所有变量 import sys wahaha = 'hahaha' print(getattr(sys.modules['__main__'],'wahaha'))#hahaha #4总结:【记】 class a: role = '治疗' def __init__(self): self.name = 'alex' self.age = 84 def func(self): print('wahaha') return 666 a = a() print(getattr(a,'name')) #alex # 反射对象的实例变量 print(getattr(a,'func')()) # 函数地址 + () wahaha 666 # 反射对象的绑定方法 print(getattr(a,'role')) # 治疗 import a # 引用模块中的任意的变量 print(getattr(a,'sww'),a.sww)#<function sww at 0x0000011b59b89b70> <function sww at 0x0000011b59b89b70> getattr(a,'sww')() #爽歪歪 print(getattr(a,'lst'),a.lst)#[1, 2, 3, 4, 5] [1, 2, 3, 4, 5] print(getattr(a,'dic'),a.dic)#{'k': 'v'} {'k': 'v'} print(getattr(a,'we'),a.we) #<a.wechat object at 0x0000011b59b8e748> <a.wechat object at 0x0000011b59b8e748> import sys # 反射本模块中的名字 cat = '小a' dog = '小b' def pig(): print('小p') print(getattr(sys.modules['__main__'],'cat'))#小a print(getattr(sys.modules['__main__'],'dog'))#小b getattr(sys.modules['__main__'],'pig')() #小p #5.反射应用的例子: class payment:pass class alipay(payment): def __init__(self,name): self.name = name def pay(self,money): dic = {'uname':self.name,'price':money} print('%s通过支付宝支付%s钱成功'%(self.name,money)) class wechat(payment): def __init__(self,name): self.name = name def pay(self,money): dic = {'username':self.name,'money':money} print('%s通过微信支付%s钱成功'%(self.name,money)) class apple(payment): def __init__(self,name): self.name = name def pay(self,money): dic = {'name': self.name, 'number': money} print('%s通过苹果支付%s钱成功' % (self.name, money)) class qqpay: def __init__(self,name): self.name = name def pay(self,money): print('%s通过qq支付%s钱成功' % (self.name, money)) import sys def pay(name,price,kind): #归一化合计 class_name = getattr(sys.modules['__main__'],kind) #关键1 obj = class_name(name) #关键2 obj.pay(price) #关键3 这三行替代了下面被注释的代码 # if kind == 'wechat': # obj = wechat(name) # elif kind == 'alipay': # obj = alipay(name) # elif kind == 'apple': # obj = apple(name) # obj.pay(price) pay('alex',400,'wechat') pay('alex',400,'alipay') pay('alex',400,'apple') pay('alex',400,'qqpay') #结果: # alex通过微信支付400钱成功 # alex通过支付宝支付400钱成功 # alex通过苹果支付400钱成功 # alex通过qq支付400钱成功 #6: class a: role = '治疗' def __init__(self): self.name = 'alex' self.age = 84 def func(self): print('wahaha') return 666 a = a() print(hasattr(a,'sex'))#false print(hasattr(a,'age'))#true print(hasattr(a,'func'))#true if hasattr(a,'func'): # if callable(getattr(a,'func')): #可调用的 getattr(a,'func')() #wahaha #7.反射的例子:文件操作 # 文件操作 class file: lst = [('读文件','read'), ('写文件','write'), ('删除文件','remove'),( '文件重命名','rename'), ('复制','copy'),('移动文件','move')] def __init__(self,filepath): self.filepath = filepath def write(self): print('in write func') def read(self): print('in read func') def remove(self): print('in remove func') def rename(self): print('in rename func') def copy(self): print('in copy func') def move(self): print('in 移动文件 func') f = file('ashkgkfj') while true: for index,opt in enumerate(file.lst,1): print(index,opt[0]) num = int(input('请输入您要做的操作序号>>')) if hasattr(f,file.lst[num-1][1]): getattr(f,file.lst[num-1][1])()
总结:
1. super: super遵循的是mro算法 只在新式类中能使用 py2新式类中需要自己添加参数(子类名,子类对象) 2. 封装: 广义上的封装 狭义上的封装 __名字 私有化: 方法名私有化 实例变量私有化 静态变量私有化 私有化的特点: 只能在类的内部使用,不能在外部使用 私有的各种静态变量和方法能不能继承: 不能被子类继承 3. 内置函数 [1]判断一个变量是不是可调用的,判断这个变量后面能不能加括号 # callable(名字) [2]装饰器 【1】 @property 把一个方法伪装成属性,使它调用的时候不用加括号 (重点) # 给伪装成属性的方法赋值 @函数名.setter装饰器 【2】 @classmethod *****(重点) # 什么时候用? # 怎么定义? # 装饰器怎么加? # 参数怎么改? # 怎么用? # 用谁来调用? 【3】 @staticmethod * # 什么时候用? # 帮助我们把一个普通的函数挪到类中来直接使用,制造静态方法用的 # 怎么定义? # 怎么用?用谁来调用? [3]反射相关 hasattr getattr 字符串数据类型的变量名,采用getattr(对象,'变量名')获取变量的值