python学习笔记day7(面向对象进阶)
一.封装小知识点回顾:
看以下代码打印内容:
class A: def __init__(self): self.__func() def __func(self): print('in A') class B(A): def __func(self): print('in B') b1 = B()
可以看出,当实例化一个b1对象时,会自动调用__init__方法,此时B类中并没有这个方法,因此会去他的父类A里边找,此时找到了__init__方法,并且他的执行代码为:self.__func(),当解释器遇到__func方法时,会将他自动加载到内存中,并将他改名为_A__func(),接下来程序就会调用这个方法,然后打印出 in A.
二.字段、方法、属性
方法的分类:
(1)普通方法属性一般由对象直接调用:
class A : name = 'alex' def __init__(self,name,age): self.name = name self.age = age def func(self): return 666 p1 = A("oldboy",1000) print(p1.name) print(p1.age) print(p1.func())
oldboy
1000
666
(2)类方法一般由类名直接调用:
class A : name = 'alex' def __init__(self,name,age): self.name = name self.age = age @classmethod def func(self): return 666 print(A.func())
注意此处的@classmethod就代表类方法,由类名直接调用即可,因此本题打印结果为:666
(3)@property方法,是将类的属性封装成方法,虽然代码上看没什么提高,但她似乎更合情合理,具体代码如下:
比如要求身高与体重的合集也就是我们通常说的bmi参数值:
class People: def __init__(self, name, weight, height): self.name=name self.__weight=weight self.__height=height @property def bmi(self): return self.__weight / self.__height**2 p1 = People('天', 95,1.83) print(p1.bmi)
此处加了一个@property方法,我们就可以把bmi方法当做一个属性,通过一个对象来调用,因此打印结果为28.367523664486843
接下来我们来看这样两个方法的用法:setter 和 deleter
class People: def __init__(self, name, age, sex): self.name = name self.__age = age self.__sex = sex @property def age(self): return self.__age @age.setter def age(self,new_age): if type(new_age) == int: self.__age = new_age else: print('请输入一个整数') @age.deleter def age(self): print(666) p1 = People('天', 28, '男') print(p1.name) p1.name = "地" print(p1.name) print(p1.age) #触发了@property方法 p1.age = "1234" #触发了@age.setter方法 del p1.age #触发了@age.deleter方法
如上当我们对age设定值的时候,就会自动调用@age.setter方法,判断是否符合age的设计格式,当我们删除age对象的时候就会自动调用@age.deleter方法,以下是打印结果:

小案例练习:用上述方法实现一个超市商品打折的需求:苹果原价5元,打8折,后因双11将原价改为8元:
class Goods: def __init__(self,name,price,count): self.name = name self.__price = price self.__count = count @property def money(self): return self.__price * self.__count @money.setter def money(self,new_price): self.__price = new_price app = Goods("apple",5,0.8) print(app.money) app.money = 8 print(app.money)
如上就实现了这样一个案例,其打印结果为:

(4).类方法:一般是由类名调用.有些情况,对于类内部的方法,无需对象直接调用,而类名直接调用即可。
class Goods: __discount = 0.8 def __init__(self,name,origin_price): self.name = name self.__origin_price = origin_price @property def price(self): return self.__origin_price * Goods.__discount @classmethod def discount(cls,new_discount): Goods.__discount = new_discount p1 = Goods('apple',5) print(p1.price) # p1.discount(0.85) Goods.discount(0.85) print(p1.price)
打印结果为:

(5).静态方法:不需要传入对象,和类名,直接调用即可.用到了@staticmethod方法
class A: def __init__(self): pass @staticmethod def login(username,password): print(username,password) A.login('alex',123)
三.接口类和抽象类
(1).接口类和抽象类是一种规范,写代码时的一种规范,接下来我们通过代码一步步深入:
版本1:设计两个类,qq和支付宝分别支付:
class Qqpay: def pay(self,money): print("您通过QQ支付%s"%money) class Alipay: def pay(self,money): print("您通过支付宝支付%s"%money) qq = Qqpay() qq.pay(100) ali = Alipay() ali.pay(200)
接下来我发现上述版本支付方式不统一,我想要一个版本统一的方法:
版本2:归一化设计,规范支付方式:
class Qqpay: def pay(self,money): print("您通过QQ支付%s"%money) class Alipay: def pay(self,money): print("您通过支付宝支付%s"%money) def pay(obj,money): obj.pay(money) qq = Qqpay() ali = Alipay() pay(qq,100) pay(ali,200)
以上虽然重新定义了一个方法规范了支付方式,但存在另一个问题,假设你的程序给别人使用,别人要重新开发一个微信支付的接口,而恰巧这个人就是野生程序员呢?他不会管你之前的功能,只会自己重新开发一个微信支付功能,程序这样设计:
版本3:野生程序员-->开发微信支付功能:
class Qqpay: def pay(self,money): print("您通过QQ支付%s"%money) class Alipay: def pay(self,money): print("您通过支付宝支付%s"%money) class Weachat: def fuqian(self,money): print("您通过微信支付%s"%money) def pay(obj,money): obj.fuqian(money) qq = Qqpay() ali = Alipay() we = Weachat() pay(qq,100) pay(ali,200) pay(we,300)
上述程序虽然实现了开发一个微信支付功能但她改变了原来的两种支付方式,导致程序无法调用到原来的支付方式,因此这个版本不行。
版本4:不能改变原来支付方式:
class Qqpay: def pay(self,money): print("您通过QQ支付%s"%money) class Alipay: def pay(self,money): print("您通过支付宝支付%s"%money) class Weachat: def pay(self,money): print("您通过微信支付%s"%money) def pay(obj,money): obj.pay(money) qq = Qqpay() ali = Alipay() we = Weachat() pay(qq,100) pay(ali,200) pay(we,300)
可以看出,这个代码虽然实现了不改变原来的支付方式,也实现了重新开发了一个支付功能,但还有一个问题就是:对于刚接手地方的程序员并不知道你之前的开发方式,这样不好。
版本5:接口类抽象类
from abc import ABCMeta,abstractclassmethod class Payrole(metaclass=ABCMeta): # 抽象类或者接口类,制定规范,统一方法名。 @abstractclassmethod def pay(self): pass class Qqpay: def pay(self,money): print("您通过QQ支付%s"%money) class Alipay: def pay(self,money): print("您通过支付宝支付%s"%money) class Weachat: def pay(self,money): print("您通过微信支付%s"%money) def pay(obj,money): obj.pay(money) qq = Qqpay() ali = Alipay() we = Weachat() pay(qq,100) pay(ali,200) pay(we,300)
这样在代码的起始,我们用这个方法制定规范,统一了方法名,让后续的开发者一看便知,以上就是接口类的具体实现。
四.其它成员方法
1.isinstance() 判断对象是否属于这个类,或者跟这个类血缘关系.
class C: pass class B(): pass class A(B): pass abj = A() print(isinstance(abj,A)) #True print(isinstance(abj,C)) #False
issubclass() 判断 第一类是第二个类的后代:
class C: pass class B(C): pass class A(B): pass abj = A() print(issubclass(A,B)) #True print(isinstance(A,C)) #False
2.反射(重要)
(1)用在类中:
看这样一段代码,如何通过输入变量名role的值呢?以往的方法时这样
class A: role = 'Teacher' def func(self): print(666) msg = input('>>>') #输入role print(A.role) #teacher print(A.__dict__[msg]) #teacher
用反射应该怎样实现呢:
class A: role = 'Teacher' def func(self): print(666) msg = input('>>>') #输入role print(getattr(A,msg)) #通过字符串去到类中获取相应的值 teacher hasattr(A,msg) print(hasattr(A,msg)) #通过字符串到类中判断是否存在此值 True #通过字符串判断此值是否存在,存在则打印 if hasattr(A,msg): print(getattr(A,msg)) else: print("不存在此值") # setattr() #增加或者修改 setattr(A,"name","alex") #增加属性 setattr(A,"role","student") #修改原有属性 print(A.__dict__) #查看属性 # delattr() #删除属性 delattr(A,"role")
(2)用在对象中:
class A: role = "teacher" def __init__(self,name,age): self.name = name self.age = age a = A("太白",18) print(hasattr(a,"name")) #判断是否存在 True print(hasattr(a,"sex")) #判断是否存在 False print(getattr(a,"age")) #得到属性值 setattr(a,"name","alex") #修改属性值 print(getattr(a,"name")) #得到属性值
在python中,一切皆对象,凡是通过什么.什么调用的,都能用反射实现
(3).用在模块中
import time print(time.time()) print(getattr(time,"time")())
import oldboy getattr(oldboy,"func")() #方式一: print(getattr(oldboy,"A")) print(getattr(oldboy,"A").role) print(getattr(oldboy,"A").func1) # 方式二 print(getattr(getattr(oldboy,'A'),'role')) #oldboy.py def func(): name = 'alexsb' print('此函数完成的是打印666功能') class A: role = 'Teacher' def func1(self): print(666)
(4).用在当前模块中:
def login(): print(666) msg = input('>>>') import sys getattr(sys.modules[__name__],msg)()
(5).__len__方法的使用:
class A: def __init__(self): self.a = 1 self.b = 2 self.c = 3 def __len__(self): return len(self.__dict__) t = A() print(len(t))
上述在调用len(t)时,自动触发__len__方法,并调用,因此返回结果为3;
(6).__hash__方法的使用:
class A: def __init__(self): self.a = 1 self.b = 2 def __hash__(self): return hash(self.a+self.b) a = A() print(hash(a)) #1+2 print(hash([1,2,3])) #TypeError: unhashable type: 'list'
上述在调用hash(a)时,自动触发__hash__方法,因此返回结果1+2
(7).__str__方法的使用:
class A: def __init__(self): self.a = 1 self.b = 2 def __str__(self): return "666" a = A() print("%s"%a)
__str__方法:当打印对象时自动触发__str__方法,因此打印结果为666
(8).__repr__方法的使用:
class A: def __init__(self): pass def __repr__(self): return "666" a = A() print(repr(a)) #666 print("%r"%a) #666
当调用repr(a)方法时自动触发__repr__方法,因此打印结果为666
(9).__call__方法的使用:
class A: def __init__(self): pass def __call__(self, *args, **kwargs): print("__call__") obj = A() obj()
当实例化一个对象的时候,通过对象()的方式自动触发__call__方法
(10).__eq__方法的使用:
class A: def __init__(self): self.a = 1 self.b = 2 def __eq__(self,obj): if self.a == obj.a and self.b == obj.b: return 666 a = A() b = A() print(a == b)
判断两个对象是否相等,执行a==b 自动触发__eq__方法
(11).__del__方法的使用:
class A: def __init__(self): self.a = 1 self.b = 2 def __del__(self): # 析构方法 print(222) return 666 obj1 = A()
3.单例模式:一个类只能实例化一个对象
class A: __intance = None def __new__(cls, *args, **kwargs): if cls.__intance is None: obj = object.__new__(cls) cls.__intance = obj return cls.__intance a = A() b = A() print(a) print(b)
上述程序就实现了一个类只能实例化一个对象。
其他方法:
class Foo: def __init__(self,name): self.name=name def __getitem__(self, item): print(self.__dict__[item]) def __setitem__(self, key, value): print(key,value) self.__dict__[key]=value def __delitem__(self, key): print('del obj[key]时,我执行') # self.__dict__.pop(key) f1 = Foo('sb') f1['name'] # 对对象进行字典式的查询操作 触发 __getitem__ f1['age'] = 18 # 对对象进行字典式的更改 触发 __setitem__ f1['age1']=19 del f1.age1 del f1['age'] # 对对象进行字典式的del删除 触发 __delitem__ f1['name']='alex' print(f1.__dict__)
五.多态,鸭子类型
Python默认就支持多态,自带多态,也叫鸭子类型。
from abc import ABCMeta,abstractmethod class Payrole(metaclass=ABCMeta): # 抽象类或者接口类,制定规范,统一方法名。 @abstractmethod def pay(self): pass class QQpay(Payrole): def pay(self,money): print('您已经通过qq支付了%s元' %money) class Alipay(Payrole): def pay(self, money): print('您已经通过支付宝支付了%s元' % money) def func(self): pass class Wechatpay(Payrole): def pay(self, money): print('您已经通过支付宝支付了%s元' % money) def pay(obj,money): obj.pay(money)
python 对于一些相似的方法,不用强制规定,都是约定俗成。
class Str: def index(self): print('通过元素找索引') class List: def index(self): print('通过元素找索引') class Tuple: def index(self): print('通过元素找索引')
六.封装
1.知识点回顾:
class Person: def __init__(self,name,age): self.name = name self.age = age def func(self): print(self.name) p1 = Person("太白",23) p2 = Person("alex",38) p1.func()
# 广义封装:封装到对象中的属性是一种封装。
# 狭义封装:私有性封装。
# 私有:私有静态字段(私有静态变量),私有普通字段(私有对象属性),私有方法
(1).私有静态字段(私有静态变量)
class Person: country = "china" #公有静态字段(静态变量) __name = "oldboy" #私有静态字段(静态变量) __age = 18 #私有静态字段(静态变量) def func(self): return self.__name print(Person.country) # print(Person.__name) 在类的外部不能调用 p1 = Person() # print(p1.__name) #也不能通过实例化对象调用 print(Person.__dict__) print(Person._Person__name) #虽然可以但最好不要使用这种方法 print(p1.func()) #在类的内部可以访问
以上凡是带有__的私有静态属性,在类的外部均不可以调用的到,只能在类的内部进行访问。
(2).派生类也不可访问父类的私有静态字段
class God: __kind = "黄皮肤" class Person(God): country = "china" #公有静态字段(静态变量) __name = "oldboy" #私有静态字段(静态变量) __age = 18 #私有静态字段(静态变量) def func(self): return self.__kind p1 = Person() print(p1.func()) #不能访问
# 私有静态字段:类内部可以访问,父类以及类的外部不能访问。
# 只要类执行,将类的内容加载到内存时,发现有 __变量名 这样的Python解释器自动将__变量名
# 转化成_类名__变量名
(3).# 私有普通字段:类内部可以访问,父类以及类的外部不能访问。
# 只要类执行,将类的内容加载到内存时,发现有 __变量名 这样的Python解释器自动将__变量名
# 转化成_类名__变量名
class Person: def __init__(self,name,age): self.name = name #公有普通字段 self.__age = age #私有普通字段 def func(self): return self.__age p1 = Person("alex",1000) print(p1.func())
(4)私有成员在哪?
class Cipher_encryption: def __init__(self,username,password): self.username = username self.__password = self.__encryption(password) def __encryption(self,pwd): ''' 加密处理 :param pwd: :return: ''' return '%s hao123' % pwd user1 = Cipher_encryption('alex', 'alexsb') print(user1.username) print(user1._Cipher_encryption__password) print(user1.__dict__)
对于只想让类内部使用的成员,要设置成私有成员。

浙公网安备 33010602011771号