date: 2021-06-25title: Python:类的相关概念 #标题
tags: #标签
categories: python # 分类
记录下python中类的语法结构,包括父类、子类的关系及其他概念….
参考:老男孩教育
类的基础语法结构
'''
子类想要调用父类的方法的同时还想执行自己的同名方法
猫和狗在调用eat的时候既调用自己的也调用父类的
在子类的方法中调用父类的方法:父类名.方法名(self)
'''
# 定义一个父类
class Animal:
def __init__(self, name, food):
self.name = name
self.food = food
self.blood = 100
self.waise = 100
def eat(self):
print(f'{self.name} is eating {self.food}...')
def drink(self):
print(f'{self.name} is drinking...')
def sleep(self):
print(f'{self.name} is sleeping...')
# 定义第一个子类(类名后面加括号,并且传入父类的名称即可)
class Cat(Animal):
def __init__(self, name, food, eye_color): # 初始化变量时,调用父类的 init 方法,自己也有init
Animal.__init__(name, food) # 调用了父类的初始化,去完成一些通用属性的初始化
self.eye_color = eye_color # 派生属性
def eat(self): # 假设猫吃了猫粮,血+100
self.blood += 100
Animal.eat(self) # 调用父类中的同名方法
self.sleep() # 调用父类中的方法
def climb_tree(self):
print(f'{self.name} is climb_tree...')
# 定义第二个子类
class Dog(Animal):
def eat(self):
self.waise += 50 # 狗吃了狗粮,智商+50
Animal.eat(self)
print(self.waise)
def kanjia(self):
print(f'{self.name} is kanjia...')
# 实例化对象
xiaogou = Dog('小黑', '狗粮')
xiaomao = Cat('小猫', '猫粮')
xiaogou.eat()
xiaomao.eat()
print(xiaomao.blood)
'''
父类和子类方法的选择:
子类的对象如果去调用方法,永远优先调用自己的
如果自己有,就用自己的,如果没有,用父类的
如果自己有,还想用父类的,那么直接在子类方法中调父类的方法:父类名.方法名(slef)
'''
新式类和经典类
在Python 2及以前的版本中,由任意内置类型派生出的类(只要一个内置类型位于类树的某个位置),都属于“新式类”,都会获得所有“新式类”的特性;反之,即不由任意内置类型派生出的类,则称之为“经典类”。
“新式类”和“经典类”的区分在Python 3之后就已经不存在,在Python 3.x之后的版本,因为所有的类都派生自内置类型object(即使没有显示的继承object类型),即所有的类都是“新式类”。
参考:官方文档。
继承顺序的区别
抽象类
所谓抽象类,就是一个开发规范,约束它的所有子类必须实现一些和它同名的方法。
我们一起看下面这个例子,原本代码中有支付宝和微信两种支付方式,并且支付的方法名都叫pay。但如果让你一个新同事再写一个苹果支付的方法,他没有给方法定义名称为pay,这样在后续调用会有不必要的麻烦,此时,我们就可以借助抽象类,来约定一些类中必须有哪些方法。
自定义抽象类
# 定义一个抽象类Payment(这个抽象类什么也不做,就是来定义开发规范的,
# 不过我为了简化代码,将name的接收值也放到了这里。)
class Payment:
def __init__(self, name):
self.name = name
def pay(self, money):
raise NotImplementedError('请在子类中重写同名pay方法')
# 接下来的每个支付类都要继承上面的抽象类Payment
class WeChatpay(Payment):
def pay(self, money):
l1 = {'uname': self.name, 'price': money}
print(f"用户{l1['uname']}通过微信支付{l1['price']}成功...")
class Alipay(Payment):
def pay(self, money):
l1 = {'uname': self.name, 'price': money}
print(f"用户{l1['uname']}通过支付宝支付{l1['price']}成功...")
class Applepay(Payment):
def pay(self, money): # 如果将这个方法定义名称不是pay,那么就会报上面抽象类返回的错误
l1 = {'uname': self.name, 'price': money}
print(f"用户{l1['uname']}通过苹果支付{l1['price']}成功...")
# 其原理是,在这里找不到pay方法,就会去父类中找
def pay(name, price, kind):
if kind == 'Wechatpay':
obj = WeChatpay(name)
elif kind == 'Alipay':
obj = Alipay(name)
elif kind == 'Applepay':
obj = Applepay(name)
obj.pay(price)
pay('lv', 500, 'Alipay')
pay('lv', 500, 'Wechatpay')
pay('lv', 500, 'Applepay')
# 将上面的代码通过反射来实现,可以更加简洁
import sys
class Payment:
def __init__(self, name):
self.name = name
def pay(self, money):
raise NotImplementedError('请在子类中重写同名pay方法')
# 接下来的每个支付类都要继承上面的抽象类Payment
class WeChatpay(Payment):
def pay(self, money):
l1 = {'uname': self.name, 'price': money}
print(f"用户{l1['uname']}通过微信支付{l1['price']}成功...")
class Alipay(Payment):
def pay(self, money):
l1 = {'uname': self.name, 'price': money}
print(f"用户{l1['uname']}通过支付宝支付{l1['price']}成功...")
class Applepay(Payment):
def pay(self, money): # 如果将这个方法定义名称不是pay,那么就会报上面抽象类返回的错误
l1 = {'uname': self.name, 'price': money}
print(f"用户{l1['uname']}通过苹果支付{l1['price']}成功...")
# 其原理是,在这里找不到pay方法,就会去父类中找
def pay(name, price, kind):
this_modules = sys.modules['__main__']
if hasattr(this_modules, kind): # 判断模块中是否存在此属性或方法
obj = getattr(this_modules, kind)(name) # 获取kind对应的值,然后实例化对象
obj.pay(price)
pay('吕建钊', 500, 'Alipay')
pay('吕建钊', 500, 'WeChatpay')
pay('吕建钊', 500, 'Applepay')
python内置的抽象类
在上面的自定义抽象类中,还可以使用另一种方式来实现,就是通过python内置的模块来实现。
# 导入模块
from abc import ABCMeta, abstractmethod
# 定义metaclass
class Payment(metaclass=ABCMeta):
def __init__(self, name):
self.name = name
@abstractmethod # 给需要规范的模块添加此装饰器
def pay(self, money):
raise NotImplementedError('请在子类中重写同名pay方法')
class WeChatpay(Payment):
def pay1(self, money):
l1 = {'uname': self.name, 'price': money}
print(f"用户{l1['uname']}通过微信支付{l1['price']}成功...")
# 会发现如果类中没有pay方法,连实例化都做不了,更不用说调用方法了
WeChatpay('alex')
# 报错如下:
Traceback (most recent call last):
File "F:/software/llvjianzhao_python/python_demo/demo_01.py", line 21, in <module>
WeChatpay('alex')
TypeError: Can't instantiate abstract class WeChatpay with abstract methods pay
类中的super方法
class User:
def __init__(self, name):
self.name = name
# 在单继承的程序中,super就是找父类的方法
class VIP_User(User): # 如果要使用super方法,就必须要使用继承
def __init__(self, name, level, start_date, end_date):
# User.__init__(self,name) # 一般不用这种方式
# super().__init__(name) # 如果只在Python3中运行,推荐这种写法
super(VIP_User, self).__init__(name) # 如果还要在python2中运行,则必须使用这种方法
self.level = level
self.start_date = start_date
self.end_date = end_date
user = VIP_User('吕建钊', 6, '2020-01-01', '2022-01-01')
print(user.__dict__)
类的封装
'''
封装就是将我们不希望暴露到外部的内容进行私有化,
所有的私有化都是为了让用户不能在外部调用类中的某个名字
一般我们很少去定义一些私有化的方法,可能会在源码中看到一些
'''
import hashlib
class User:
__default_name = '张三' # 私有的静态变量
def __init__(self, name, pwd):
self.name = name
self.__pwd = pwd # 私有的实例变量
def __get_md5(self): # 私有的绑定方法
md5 = hashlib.md5(self.name.encode('utf-8'))
md5.update(self.__pwd.encode('utf-8'))
return md5.hexdigest()
def get_pwd(self):
print(self.__default_name)
return self.__get_md5()
user = User('lv', '123.com')
# print(user.__default_name) # 无法直接调用私有的变量
print(user.get_pwd())
print(user.__dict__) # 输出:{'name': 'lv', '_User__pwd': '123.com'}
# 可以看到Python将私有变量名__pwd改成了_User__pwd(user为类名)
# 根据规律,我们可以通过如下方式调用私有变量
print(user._User__default_name)
print(user._User__pwd)
property将方法伪装成一个属性
原代码
# 看下面的代码,是一个求面积的类
from math import pi
class A:
def __init__(self, r):
self.r = r # 定义半径
def area(self): # 求面积
return pi * self.r ** 2
c1 = A(5)
print(c1.r) # 打印半径
print(c1.area()) # 打印面积
# 在我们的认知中,这个圆形的半径或面积,都应该是一个属性
# 而登陆或注册等功能,才是方法
# 所有最好可以将求面积这个功能伪装成一个属性,而不是方法
将方法伪装成属性的代码
from math import pi
class A:
def __init__(self, r):
self.r = r # 定义半径
@property # 把一个方法伪装成属性,在调用这个方法的时候,不需要加()就可以直接调用
def area(self): # 求面积
return pi * self.r ** 2
c1 = A(5)
print(c1.r) # 打印半径
print(c1.area) # 打印面积,可以不在方法名后加括号,像调用属性一样调用方法