一、继承
继承存在的意义:将公共的方法提取到父类中,有利于增强代码的重用性。调用类中成员时应遵循:优先在自己所在的类中找,没有的话就去父类中找;吐过存在多个父类时,则先找左边的再找右边的。
(一)mro和c3算法
1、mro
如果类中存在继承关系,则可以通过mro()获取当前类的继承关系(找成员的顺序)。
class C():
pass
class B():
pass
class A(B, C):
pass
print(A.mro())#[<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>]
print(A.__mro__) #(<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>)
2、c3算法
对于上述这个继承关系来说,C3算法的计算规则是:
mro(A) = [A] + merge(mro(B), mro, [B, C] )
接着要把B和C的继承关系填写出来
mro(A) = [A] +merge([B,object], [C,object], [B, C] )
C3算法是:先把先读取第一个B,然后查阅B和后面两个参数(C,object),[B, C] 里面除了第一个参数以外的其他参数(object和C)的关系,结果发现(object和C)中都不含B,所以就把B单独提取出来,并把其他参数中包含的B剔除。
mro(A) = [A] + [B] + merge(mro(object), mro(C,object), [C] )
接着按照算法的规则进行判别:
mro(A) = [A] + [B, C] + merge(mro(object), mro(object) )
mro(A) = [A] + [B, C, object]
mro(A) = [A, B, C, object]
mro(A) = [A, B, C]
mro(A) = [A] +mro(mro(B) +mro[C], [B, C])
mro(A) = [A] +mro([B], [C, D] , [B, C])
mro(A) = [A] +[B] + mro( [C, D] , [C])
mro(A) = [A] +[B, C]+ mro( [D] )
mro(A) = [A] +[B, C, D]
mro(A) = [A, B, C, D]
mro(A) = [A] +mro(mro(B) +mro[C], [B, C])
mro(A) = [A] +mro([B, D] , [C], [B, C])
mro(A) = [A] , [B], mro([D] , [C], [C])
mro(A) = [A] , [B, D], mro([C], [C])
**mro(A) = [A] , [B, D, C] **
**mro(A) = [A, B, D, C] **
mro(A) = [A] +mro(mro(B) +mro[C], [B, C])
mro(A) = [A], mro([B, D], [C, D], [B, C])
mro(A) = [A], [B], mro([D], [C, D], [C])
此时,我们就需要找D了,寻找其他参数中除了第一个参数以外的参数是否包含D,由于存在【C, D】,所以D不能提取出来,接着只能对比C,发现其他参数中不含有C,所以把C提出来
mro(A) = [A], [B, C], mro([D], [D])
**mro(A) = [A], [B, C, D] **
**mro(A) = [ A, B, C, D] **
mro(A) = [A], mro(mro(B), mro[C], mro[P], [B, C, P])
mro(B) = [B], mro(mro[D], mro[E], [D, E])
mro(D) = [D], mro(mro[G], mro[H], [G, H])
mro(D) = [D], mro([G], [H, K], [G, H])
mro(D) = [D], [G], mro([H,k], [H])
mro(D) = [D], [G, H], mro([k] ])
mro(D) = [D], [G, H, K ]
mro(D) = [D, G, H, K ]
mro(E) = E + mro(M)
mro(E) = [E, M]
mro(B) = [B], mro([D, G, H, K ], [E, M], [D, E])
mro(B) = [B], [D], mro([G, H, K ], [E, M], [E])
mro(B) = [B], [D, G], mro([H, K ], [E, M], [E])
mro(B) = [B], [D, G, H], mro([K], [E, M], [E])
mro(B) = [B], [D, G, H, K], mro([E, M], [E])
mro(B) = [B, D, G, H, K, E, M]
mro =[C], mro(mro(E), mro(F), [E,F])
mro =[C], mro([E, M], mro([F], mro[mor[M], mro[H], [M, H]), [E,F])
mro =[C], mro([E, M], [F, M, N], [E,F])
mro =[C], [E], mro([M], [F, M, N], [F])
mro =[C], [E, F], mro([M], [M, N])
mro =[C, E, F, M, N]
mro(A) = [A], mro([B, D, G, H, K, E, M], [C, E, F, M, N], [B, C, P])
mro(A) = [A], [B], mro([D, G, H, K, E, M], [C, E, F, M, N], [C, P])
mro(A) = [A], [B, D, G, H, K, C, E, F, M, N, H, P]
mro(A) = [A, B, D, G, H, K, C, E, F, M, N, P, object]
特别补充,继承父类关系:从左到右、深度优先,大小钻石,留住顶端
(二)py2和py3区别(了解即可)
在py2.2之前,只支持经典类(从左到右,深度优先,大小钻石,不留顶端),后来python希望所有类都默认继承object类,此时发现会有Bug,所以就再次创建了一个新式类支持这个功能:从左到右,深度优先,大小钻石,保留顶端。
在py2.2及之前,就有了经典类(不继承object)和新式类(直接或间接继承object)两种。
#经典类
class Foo():
pass
#新式类
class base(object):
pass
class Foo(base):
pass
最终在py3中丢弃了经典类,只保留了新式类。
二、内置函数补充
类方法:classmethod、 静态方法:statisticmethod、属性:property
(一)callable:是否可以在后面加括号执行
一般来说,可以加括号执行的有:函数、类和带有__call__方法的实例对象
#1、函数
def func():
pass
print(callable(func)) #True
#2、类
class Foo():
pass
print(callable(Foo)) #True
#3、类中具有__call__方法的对象
class Foo(object):
def __call__(self, *args, **kwargs):
pass
v1 = Foo()
print(callable(v1)) #True
(二)super():按照mro继承关系向上找成员
class Base(object):
def message(self, num):
print("Base.message", num)
class Foo(Base):
def message(self, num):
print("Foo.message", num)
super().message(num+100)
obj = Foo()
print(obj.message(1))
''' Foo.message 1 Base.message 101 '''
根据上下述代码可以看出来,Foo类的父类是Base和Bar,先找到Base的父类中的message方法,执行该方法,由于有super方法,所以会向上找Base父类的message方法,但是由于这里的self是Foo()方法的实例对象,因此会向Foo()父类中找,Base方法没有,Bar方法中有message,于是继续调用Bar中的message方法。
class Base(object):
def message(self, num):
print("Base.message", num)
super().message(1000)
class Bar(object):
def message(self, num):
print("Bar.message", num)
class Foo(Base, Bar):
pass
obj = Foo()
print(obj.message(1))
''' Base.message 1 Bar.message 1000 '''
应用场景:假设有一个类,他原本已经实现了某些功能,但我们想在原有代码基础上实现某些功能,此时就不必再次重写代码,只需要使用super()方法调用上游方法。
比如,我们可以改写字典中的get()函数功能:
info = dict()
info['name'] = "高宇星"
info['age'] = 18
print(info)
#{'name': '高宇星', 'age': 18}
value = info.get('age')
print(value) #18
#此时我们想在原有字典功能的基础上改写get功能
class Mydict(dict):
def get(self, k):
print("自定义功能")
return super().get(k)
info = Mydict()
info['name'] = "高宇星"
info['age'] = 18
print(info)
value = info.get('age')
print(value)
''' 自定义功能 18 '''
在实际开发的过程中,可以通过super()函数来寻找上游的方法。
(三)type():获取一个对象的类型
v1 = "高宇星今年18岁"
result = type(v1)
print(result)
#<class 'str'> 这里的str其实是类名称
#定义一个类
class Foo():
pass
#实例化一个对象
v3 = Foo()
#查询该对象的类型
print(type(v3))
#<class '__main__.Foo'>
(四)isinstance():判断对象是否是某个类或其子类的实例
class Top(object):
pass
class Base(Top):
pass
class Foo(Base):
pass
v_ = Foo()
print(isinstance(v_, Foo)) #True
print(isinstance(v_, Base))#True
print(isinstance(v_, Top)) #True
应用场景:简化代码
class Animal(object):
def run(self):
print("123456")
class Dog(Animal):
pass
class Cat(Animal):
pass
data_list = ['alex', Dog(), Cat(), 'root']
#在不使用isinstance方法时:
for item in data_list:
if type(item) == Cat:
item.run()
elif type(item) == Dog:
item.run()
else:
pass
#在使用isinstance函数时:
for item in data_list:
if isinstance(item, Animal):
item.run()
else:
pass
(五)issubclass():判断是否是某个类的子孙类
class Top(object):
pass
class Base(Top):
pass
class Foo(Base):
pass
print(issubclass(Foo, Top)) #True
print(issubclass(Foo, Base))#True
三、异常处理
当我们在执行代码时,可能会出现一些不可知的错误,当我们不想让它报错,继续执行下面的代码时,可以选择用异常处理来做。
while True:
url = input("请输入要下载的网页地址")
try:
res = requests.get(url=url)
except Exception as e:
print("请求失败的原因是:{}".format(str(e)))
with open('content.txt', mode='wb') as f:
f.write(res.content)
当运行无误时:
请输入要下载的网页地址http://www.baidu.com
请输入要下载的网页地址
当关掉网络再次运行时:
请求失败的原因是:HTTPConnectionPool(host='www.baidu.com', port=80): Max retries exceeded with url: / (Caused by NameResolutionError("<urllib3.connection.HTTPConnection object at 0x00000282B35E4520>: Failed to resolve 'www.baidu.com' ([Errno 11001] getaddrinfo failed)"))
请输入要下载的网页地址
以后常用的应用场景:
- 调用微信的API实现微信消息的推送、微信支付等;
- 支付宝支付、视频播放等;
- 数据库或redis连接和操作;
- 调用第三方的视频播放功能,由第三方的程序出问题导致的错误。
异常处理的基本格式:
try:
#逻辑代码
except Exception as e:
#try中的代码如果有异常,则此代码块的代码会执行
try:
#逻辑代码
except Exception as e:
#try中的代码如果有异常,则此代码块的代码会执行
finally:
#try中的代码无论报错与否,finally中的代码都会执行,一般用于释放资源
(一)异常细分
如果想要对异常进行更细致得异常处理,可以这样做:
import requests
from requests import exceptions
while True:
url = input("请输入要下载的网页地址")
try:
res = requests.get(url=url)
print(res)
except exceptions.MissingSchema as e:
print("URL架构不存在")
except exceptions.InvalidSchema as e:
print("URL架构错误")
except exceptions.InvalidURL as e:
print("URL地址格式错误")
except exceptions.ConnectionError as e:
print("网络连接错误")
except Exception as e:
print("代码错误", e)
#提示:如果想些简单一点,可以只写一个Exception捕获错误就可以。
(二)自定义异常&抛出异常
python内置异常,只有在遇到特定的错误才会抛出响应的异常,其实在开发中也可以自定义异常。
class MyException(Exception):
pass
try:
pass
except MyException as e:
print("Myexception异常触发了", e)
except Exception as e:
print("Exception", e)
上述代码虽然定义了MyException,但是不会被触发,因为默认的那些异常有特定的触发条件,对于我们自定义的异常,如果想要触发,则需要使用 raise MyException()类实现。
class MyException(Exception):
pass
try:
raise MyException()
except MyException as e:
print("Myexception异常触发了", e)
except Exception as e:
print("Exception", e)
#Myexception异常触发了
当我们改变异常中初始化方法时,在异常触发的时候就会优先实现我们自己的功能,而不是从父类Exception中找。
class MyException(Exception):
def __init__(self, msg):
super().__init__()
self.msg = msg
try:
raise MyException("xxx失败了")
except MyException as e:
print("Myexception异常触发了", e.msg)
except Exception as e:
print("Exception", e)
#Myexception异常触发了 xxx失败了
或者我们可以自定义类变量,也可以实现跟上述代码相同的效果。
class MyException(Exception):
title = "请求失败"
try:
raise MyException()
except MyException as e:
print("Myexception异常触发了", e.title)
except Exception as e:
print("Exception", e)
#Myexception异常触发了 请求失败
应用场景:当合作开发时,我编写了方法,别人需要调用我的方法时就会用到异常函数;在框架内部已经定义好时,遇到什么错误就会触发不同的异常;按照规定去触发制定的异常,每种异常都具备特殊的含义。
(三)特殊的finally
伪代码:
try:
raise MyException()
except MyException as e:
print("Myexception异常触发了", e.title)
except Exception as e:
print("Exception", e)
当在函数或方法中定义异常处理的代码时,要特别注意finally和return。如果在函数中定义了return和finally,那么finally一定会在return之前执行。
def Func():
try:
return 123
except Exception as e:
pass
finally:
print(666)
a = Func()
print(a)
''' 666 123 '''
(四)练习题
1、补充代码实现捕获程序中的错误:
#迭代器
class MyInterRange():
def __init__(self, num):
self.num = num
self.counter = -1
def __iter__(self):
return self
def __next__(self):
self.counter += 1
#print(self.counter)
if self.counter == self.num:
raise StopIteration()
return self.counter
obj = MyInterRange(20)
while True:
ele = next(obj)
print(ele)
将__next__方法中的raise StopIteration()改在循环函数while中:
class MyInterRange():
def __init__(self, num):
self.num = num
self.counter = -1
def __iter__(self):
return self
def __next__(self):
self.counter += 1
#print(self.counter)
if self.counter == self.num:
raise StopIteration()
return self.counter
obj = MyInterRange(20)
while True:
try:
ele = next(obj)
except StopIteration as e:
print("数据获取完毕")
break
print(ele)
2、补充代码实现捕获程序中的错误:
class MyInterRange():
def __init__(self, num):
self.num = num
self.counter = -1
def __iter__(self):
return self
def __next__(self):
self.counter += 1
#print(self.counter)
if self.counter == self.num:
raise StopIteration()
return self.counter
class Xrange(object):
def __init__(self, max_num):
self.max_num = max_num
def __iter__(self):
return self
obj = Xrange(100)
obj_iter = obj.__iter__()
while True:
ele = next(obj)
print(ele)
修改:
class MyInterRange():
def __init__(self, num):
self.num = num
self.counter = -1
def __iter__(self):
return self
def __next__(self):
self.counter += 1
#print(self.counter)
if self.counter == self.num:
raise StopIteration()
return self.counter
class Xrange(object):
def __init__(self, max_num):
self.max_num = max_num
def __iter__(self):
return MyInterRange(self.max_num)
obj = Xrange(100)
obj_iter = obj.__iter__()
while True:
try:
ele = next(obj_iter)
except StopIteration as e:
print("数据获取完毕")
break
print(ele)
3、补充代码实现捕获程序中的错误:
def func():
yield 1
yield 2
yield 3
gen = func()
while True:
ele = next(gen)
print(ele)
''' 1 2 3 '''
修改代码之后是:
def func():
yield 1
yield 2
yield 3
gen = func()
while True:
try:
ele = next(gen)
except StopIteration as e:
print("数据获取失败")
break
print(ele)
4、补充代码实现捕获程序中的错误:
num = int("高宇星")
补充后为:
try:
num = int("高宇星")
except ValueError as e:
print("转换失败")
#转换失败
5、补充代码实现捕获程序中的错误:
data = [11, 22, 33, 44, 55]
print(data[1000])
修改后:
try:
data = [11, 22, 33, 44, 55]
print(data[1000])
except IndexError as e:
print("索引错误")
#索引错误
6、补充代码实现捕获程序中的错误:
data = {
'k1': 123, "k2": 456}
print(data['xxx'])
补充后:
try:
data = {
'k1': 123, "k2": 456}
print(data['xxx'])
except KeyError as e:
print("key值不正确")
#key值不正确
7、补充代码实现捕获程序中的错误:
class MyDict(dict):
def __getitem__(self, item):
return super().__getitem__(item)
info = MyDict()
info["dididi"] = 10101
info["one"] = 100
info["liugan"] = 80
print(info["dididi"]) #10101
print(info["xxx"]) #报错了
代码补充后:
class MyDict(dict):
def __getitem__(self, item):
try:
return super().__getitem__(item)
except KeyError as e:
return None
info = MyDict()
info["dididi"] = 10101
info["one"] = 100
info["liugan"] = 80
print(info["dididi"]) #10101
print(info["xxx"]) #None
8、看代码写结果:
def run(handler):
try:
num = handler()
print(num)
return "成功"
except Exception as e:
return "错误"
finally:
print("END")
res = run(lambda: 123)
print(res)
由于在def函数中,存在return和finally的时候,一般先执行finally,后才会执行return,所以结果应该是:123——>END——>成功
def func():
print(666)
return "成功"
def run(handler):
try:
num = handler()
print(num)
return func()
except Exception as e:
return "错误"
finally:
print("END")
res = run(lambda: 123)
print(res)
当我们把之前run函数中return的“成功”定义为到另一个函数中,那么在run中的return中触发了func函数,执行了func函数中的666,而“成功”将作为func的结果传入到run的return中,在finally后执行,因此输出结果应该是 123——>666——>结束——>成功。
四、反射
反射提供了一种更加灵活的方式让我们可以实现对象中的操作成员(以字符串形式中对象中进行成员的操作 )
class Person(object):
def __init__(self, name, wx):
self.name = name
self.wx = wx
def show(self):
message = "姓名:{}, 微信:{}".format(self.name, self.wx)
return message
user_object = Person(name="高宇星", wx=1234456)
a = user_object.name
print(a)
#高宇星
#重置名字属性
user_object.name = "闫曦月"
print(user_object.name) #闫曦月
获取成员可以通过getattr这个方法来实现
User = Person("小猪佩奇", "wx_id6688")
#通过getattr获取成员
print(getattr(User, "name")) #小猪佩奇
print(getattr(User, "wx")) #wx_id6688
print(getattr(User, "show"))
#<bound method Person.show of <__main__.Person object at 0x000002669F3BFF70>>
print(getattr(User, "show")())#如果要调用方法,需要在getattr之后加括号才行
#姓名:小猪佩奇, 微信:wx_id6688
python中有4个内置函数来支持反射:
1、getattr():去对象中获取成员
2、setatrr():去对象中设置成员
3、hasatrr():对象中是否包含成员
4、delatrr():删除对象中的成员
使用案例:
class Account():
def login(self):
pass
def register(self):
pass
def index(self):
pass
account = Account()
def run(name):
name = input("请输入要执行的方法名称:")
method = getattr(account, name)
if not method:
print("输入错误")
return
print(method())
run("index")
(一)一切皆是对象
python中一切皆是对象,每个对象的内部都有自己维护的成员。
1、对象是对象
class Person(object):
def __init__(self, name, wx):
self.name = name
self.wx = wx
def show(self):
message = "姓名:{}, 微信:{}".format(self.name, self.wx)
return message
user_object = Person(name="高宇星", wx=1234456)
a = user_object.name
print(a)
2、类是对象
class Person():
title = "草泥马"
print(Person.title)
#草泥马
3、模块也是对象
import re
print(re.match) #<function match at 0x00000224A3CB5F70>
由于反射支持以字符串形式去对象中操作成员,等价于 对象.成员,所以基于反射也可以对类、模块中的成员操作。简单粗暴得讲: 只要看到XXX.XX都可以用反射实现。
class Person():
title = "草泥马"
print(Person.title)
#草泥马
print(getattr(Person, "title"))
#草泥马
import re
print(re.match) #<function match at 0x00000224A3CB5F70>
v1 = re.match("\w+", "dfjkeablal")
print(v1)
#<re.Match object; span=(0, 10), match='dfjkeablal'>
func = getattr(re, "match")
v2 = func("\w+", "dfjkeablal")
print(v2)
#<re.Match object; span=(0, 10), match='dfjkeablal'>
(二)import_module+反射
在python中如果想导入一个模块,可以通过import语法导入,其实也可以通过字符串形式导入
1、示例一
import random
from importlib import import_module
m = import_module(random)
2、示例二
from requests import exceptions
import requests
g = import_module(requests.exceptions)
3、示例三
import_module只能导入到模块级别的,比如:
from requests.exceptions import InvalidURL
k = import_module("requests.exceptions.InvalidURL")
#报错 ModuleNotFoundError: No module named 'requests.exceptions.InvalidURL'; 'requests.exceptions' is not a package
#正确引入方式:
k = import_module("requests.exceptions")
a = k.InvalidURL
在很多项目的源码中会有import_module和getattr配合实现根据字符串的形式导入模块并获取成员,例如:
from importlib import import_module
path = "openpyxl.utils.exception.InvalidFileException"
module_path, class_name = path.rsplit(".", maxsplit=1)
module_object = import_module(module_path)
cls = getattr(module_object, class_name)
print(cls)
参考示例代码:
(1)主运行文件
import config
from importlib import import_module
def run():
text = input("请输入注册信息:")
#发送消息提醒
for path in config.MESSAGE_HANDLER_LIST:
m, c = path.split(".", maxsplit=1)
module = import_module(m)
cls = getattr(module, c)
obj = cls()
obj.send()
if __name__ == '__main__':
run()
(2)配置文件
MESSAGE_HANDLER_LIST=[
"handler.email.Email",
"handler.msg.Msg",
]
(3)handler文件夹有两个类:email和msg
class Email():
def send(self):
print("发邮件")
class Msg():
def send(self):
print("发信息")
五、作业
(一)写出下面图示的类继承关系
mro(A) = [A], mro(mro(B) , mro(F) , [BF])
mro(B) = [B], mro([CM], [DE], [CD])=[B,C], mro([M], [DE], [D])= [B,C,M,E]
mro(F)=[F], mro(mro[J], mro[H], [JH])=[F], mro([JE], H, [JH]) = [F,J,E,H]
mro(A)= [A], mro( [B,C,D,M,E], [F,J,E,H],[BF])= [A,B], mro([C,D,M,E], [F,J,E,H],[F])=[A,B,C,M,D,F,J,E,H]
(二)看代码写结果
class Foo():
def __call__(self, *args, **kwargs):
return 666
data_list = [
'高宇星',
dict,
lambda:123,
True,
Foo,
Foo()
]
for item in data_list:
if callable(item):
val = item()
print(val)
else:
print(item)
''' 高宇星 字符串,不能加括号,输出字符串本身 {} dict是一个类,加括号后实例化了一个对象,但是空值,生成空字典 123 lambda是一个函数,加了括号后执行了函数本省,输出了数字123 True true不能被执行,所以输出ture本身 <__main__.Foo object at 0x0000014C4D754700> foo是一个类,加了括号后创建了对象 666 类里面有一个call函数,执行函数后输出666 '''
文章评论