目录
1. 继承补充
- 继承存在的意义: 将公共的方法提取到父类,有利于增加代码重用性
- 继承的编写方式:
# 继承
class Base(object):
pass
class Foo(Base):
pass
# 多继承
class Base(object):
pass
class Bar(object):
pass
class Foo(Base,Bar):
pass
- 调用类中的成员时遵循:
- 优先在自己所在的类去找,没有则取父类中去找
- 如果类存在多继承(多个父类),则先找左边再找右边
1.1 mro 和 c3算法
如果类中存在继承关系,可以通类.mro()
获取当前类的继承关系(找成员的顺序)
C3算法
:广度优先
示例1:
class C(object): pass
class B(object): 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'>)
# C3算法
# mro(A) 代表想要推算A的继承关系
mro(A) = [A] + merge(mro(B),mor(C),[B,C])
mro(A) = [A] + merge([B],[C],[B,C]) # 先推算[B] 是否是 [C],[B,C] 的子类(依次比对),不是则提取出来,是则跳过(保留),继续推算
mro(A) = [A] + [B,C]
mro(A) = [A,B,C]
示例2:
class D(object): pass
class C(D): pass
class B(object): pass
class A(B,C): pass
print(A.mro()) # [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>]
print(C.mro()) # [<class '__main__.C'>, <class '__main__.D'>, <class 'object'>]
# C3算法
mro(A) = [A] + merge(mro(B),mro(C),[B,C])
mro(A) = [A] + merge([B],[C,D],[B,C])
mro(A) = [A] + [B,C,D]
mro(A) = [A,B,C,D]
示例3:
class D(object): pass
class C(object): pass
class B(D): pass
class A(B,C): pass
print(A.mro()) # [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.D'>, <class '__main__.C'>, <class 'object'>]
# C3算法
mro(A) = [A] + merge(mro(B),mro(C),[B,C])
mro(A) = [A] + merge([B,D],[C],[B,C])
mro(A) = [A] + [B,D,C]
mro(A) = [A,B,D,C]
示例4:
class D(object): pass
class C(D): pass
class B(D): pass
class A(B,C): pass
print(A.mro()) # [<class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.D'>, <class 'object'>]
# C3算法
mro(A) = [A] + merge(mro(B),mro(C),[B,C])
mro(A) = [A] + merge([B,D],[C,D],[B,C])
mro(A) = [A] + [B,C,D]
mro(A) = [A,B,C,D]
示例5:
class M: pass
class N: pass
class E(M): pass
class G: pass
class K: pass
class H(K): pass
class D(G,H): pass
class F(M,N): pass
class P: pass
class C(E,F): pass
class B(D,E): pass
class A(B,C,P): pass
print(A.mro()) # A -> B -> D -> G -> H -> K -> C -> E -> F -> M -> N -> P -> object
# C3算法
mro(A) = [A] + merge(mro(B),mro(C),mro(P),[B,C,P])
--------------------------------------------------
mro(B) = [B] + merge(mro(D),mro(E),[D,E])
# mro(D) = merge(mro(G),mro(H),[G,H])
# mro(G) = [G]
# mro(H) = [H,K]
# mro(E) = [E,M]
mro(B) = [B] + merge([D,G,H,K],[E,M])
mro(B) = [B,D,G,H,K,E,K]
--------------------------------------------------
mro(C) = [C] + merge(mro(E),mro(F),[E,F])
# mro(E)= [E,M]
# mro(F) = [F,M,N]
mro(C) = [C] + merge([E,M],[F,M,N])
mro(C) = [E,F,M,N]
--------------------------------------------------
mro(P) = [P]
--------------------------------------------------
mro(A) = [A] + merge([B,D,G,H,K,E,M],[C,E,F,M,N],[P],[B,C,P])
mro(A) = [A,B,D,G,H,K,C,E,F,M,N,P]
1.2 py2 和 py3继承区别
- 在python2.2之前,只支持经典类
深度优先查找法
后来,Python想让类默认继承object(其他语言的面向对象基本上也都是默认都继承object),此时发现原来的经典类不能直接集成集成这个功能,有Bug。
所以,Python决定不再原来的经典类上进行修改了,而是再创建一个新式类来支持这个功能新式类采用的是广度优先
python2.2
之后经典类和新式类共存。(正式支持是2.3)
python3
中丢弃经典类,只保留新式类。
- 经典类,不继承object类型
class Foo:
pass
- 新式类,直接或间接继承object
class Base(object):
pass
class Foo(Base):
pass
2. 内置函数补充
-
classmethod
、staticmethod
、property
以装饰器的形式放在类方法、静态方法、绑定方法 -
callable (__call__)
:是否可以在后面加括号执行- 函数
def func(): pass
print(func) # <function func at 0x00000227CDF5C1E0>
print(callable(func)) # True
- 类
class Foo(): pass
print(callable(Foo)) # True
- 类中具有
__call__
方法的对象
class Foo():
def __init__(self): pass
def __call__(self, *args, **kwargs): pass
obj = Foo()
print(callable(obj)) # True
super
:按照mro继承关系向上找成员 (用来处理多重继承问题中直接用类名调用父类方法)
class Top(object):
def message(self, num):
print("Top.message", num)
class Base(Top): pass
class Foo(Base):
def message(self, num):
print("Foo.message", num)
super().message(num + 100) # 与self不同的是super优先去上游去找,Base中没有message方法,继续往上找 -> Top.message(num + 100)
obj = Foo()
obj.message(1)
"""
Foo.message 1
Top.message 101
"""
应用场景:
- 假如有一个类原来已实现了某些功能,但我们想再其基础上再扩展点功能,可以使用
super
class MyDict(dict):
def get(self, k):
print('自定义功能')
return super().get(k)
info = MyDict()
info['name'] = 'xxx' # 会自动触发__setitem__
info['age'] = 18 # 会自动触发__setitem__
print(info) # {'name': 'xxx', 'age': 18}
value = info.get('age')
print(value)
"""
print('自定义功能')
通过supper()从dict类返回18
"""
type
:获取一个对象的类型
v1 = 'hello'
print(type(v1)) # <class 'str'>
print(type(v1) == str) # True
v2 = ['a','b','c']
print(type(v2)) # <class 'list'>
print(type(v2) == list) # True
class Foo(object): pass
v3 = Foo()
print(type(v3)) # <class '__main__.Foo'>
print(type(v3) == Foo) # True
isinstance
:判断对象是否是某个类或其子类的实例
class Top(object): pass
class Base(Top): pass
class Foo(Base): pass
v1 = Foo()
print(isinstance(v1,Foo)) # True,对象v1是Foo类的实例
print(isinstance(v1,Base)) # True,对象v1是Base子类的实例
print(isinstance(v1,Top)) # True,对象v1是Top子类的实例
issubclass
:判断类是否是某个类的子孙类
class Top(object): pass
class Base(Top): pass
class Foo(Base): pass
print(issubclass(Foo,Base)) # True,Foo是Base的子类
print(issubclass(Foo,Top)) # True,Foo是Top的子类的子类
3. 异常处理
在程序开发中如果遇到一些 不可预知的错误
或 懒得做一些判断时,可以选择用异常处理来做
import requests
url = input("输入url: ")
res = requests.get(url=url)
with open('content.txt',mode='wb') as f:
f.write(res.content)
上述下载视频的代码在正常情况下可以运行, 但如果遇到网络问题,程序就会报错无法正常运行,此时可以使用异常处理
try:
代码块
except Exception as e:
代码块,上述代码出异常待执行
import requests
while True:
url = input("输入url: ")
try:
res = requests.get(url=url)
except Exception as e:
print("请求失败,原因{}".format(str(e)))
continue
with open('content.txt', mode='wb') as f:
f.write(res.content)
num1 = input('请输入数字:')
num2 = input('请输入数字:')
try:
num1 = int(num1)
num2 = int(num2)
result = num1 + num2
print(result)
except Exception as e:
print("输入错误,{}".format(str(e)))
常见的应用场景:
- 调用微信的API实现微信消息的推送、微信支付等
- 支付宝支付、视频播放等
- 数据库、redis连接和操作
- 调用第三方的视频播放功能,由第三方的程序出问题导致的错误
异常处理的基本格式:
try:
# 逻辑代码
except Exception as e:
# try中的代码如果有异常,则次代码块中的代码会执行
try:
# 逻辑代码
except Exception as e:
# try中的代码如果有异常,则次代码块中的代码会执行
finally:
# try中的代码无论是否报错都会执行,即便在except中执行了return终止函数也会执行,一般用于释放资源
"""
try:
file_object = open('xxx.log')
except Exception as e:
# 异常处理
finally:
# try没异常,最后执行finally关闭文件 # try有异常,先执行except中的代码,最后再执行finally中的代码(关闭文件)
file_object.close()
"""
import requests
def e():
url = input("输入url: ")
try:
res = requests.get(url=url)
print('123') # 当捕获到异常此行不会再执行
flag = 0
except Exception as e:
flag = 1
return "请求失败,原因{}".format(str(e))
finally: # 无论是否有异常都会在try之后或except之后、return之前执行
if flag == 0:
print('请求成功:')
print("请求失败")
print("aaa") # 当捕获到异常此行不会再执行
print(e())
3.1 异常细分
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)
如果想对错误进行细分的处理,例如:发生Key错误和发生Value错误分开处理
基本格式:
try:
# 逻辑代码
pass
except KeyError as e:
print("KeyError")
except ValueError as e:
print("ValueError")
except Exception as e:
print("Exception")
Python内置细分错误:
# 常见异常:
"""
AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x
IOError 输入/输出异常;基本上是无法打开文件
ImportError 无法引入模块或包;基本上是路径问题或名称错误
IndentationError 语法错误(的子类) ;代码没有正确对齐
IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问n x[5]
KeyError 试图访问字典里不存在的键 inf['xx']
KeyboardInterrupt Ctrl+C被按下
NameError 使用一个还未被赋予对象的变量
SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了)
TypeError 传入对象类型与要求的不符合
UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,
导致你以为正在访问它
ValueError 传入一个调用者不期望的值,即使值的类型是正确的
"""
# 更多异常:
"""
ArithmeticError
AssertionError
AttributeError
BaseException
BufferError
BytesWarning
DeprecationWarning
EnvironmentError
EOFError
Exception
FloatingPointError
FutureWarning
GeneratorExit
ImportError
ImportWarning
IndentationError
IndexError
IOError
KeyboardInterrupt
KeyError
LookupError
MemoryError
NameError
NotImplementedError
OSError
OverflowError
PendingDeprecationWarning
ReferenceError
RuntimeError
RuntimeWarning
StandardError
StopIteration
SyntaxError
SyntaxWarning
SystemError
SystemExit
TabError
TypeError
UnboundLocalError
UnicodeDecodeError
UnicodeEncodeError
UnicodeError
UnicodeTranslateError
UnicodeWarning
UserWarning
ValueError
Warning
ZeroDivisionError
"""
3.2 自定义异常和抛出异常
以上都是Python内置的异常,只有遇到特定的错误之后才会抛出相应的异常
在开发中也可以自定义异常:
class MyException(Exception): pass
对于自定义异常如果想要触发,需要使用 rasise MyExcetion()
实现
class MyException(Exception):
def __init__(self, msg, *args, **kwargs):
super().__init__(*args, **kwargs)
self.msg = msg
try:
# ...
raise MyException("xxx失败了") # 当遇到raise MyException("xxx失败了"),程序会立即报错,交给MyException
except MyException as e:
print("MyException被触发了", e.msg)
except Exception as e:
print('Exception被触发了', e)
class MyException(Exception):
title = "请求错误"
try:
# ...
raise MyException()
except MyException as e:
print("MyException被触发了",e.title)
except Exception as e:
print('Exception被触发了',e)
应用场景:
- 协同开发,B同事用A同事写的方法
- A同事定义了一个函数
import re
import smtplib
from email.mime.text import MIMEText
from email.utils import formataddr
class EmailValidError(Exception):
title = "邮箱格式错误"
class ContenRequireError(Exception):
title = "邮箱内容错误"
def send_email(email,content):
if not re.match("\w.+qq.com",email):
raise EmailValidError()
if len(content) == 0:
raise ContenRequireError()
"""邮件内容配置"""
# 邮件文本
msg = MIMEText(content,'html','utf-8')
# 邮件上显示的发件人
msg['From'] = formataddr(['邵冲','[email protected]'])
# 邮件上显示的主题
msg['Subject'] = '测试邮件'
"""发送邮件"""
server = smtplib.SMTP_SSL('smtp.163.com')
server.login('[email protected]','QDZJCKKEJRWNPIAK')
server.sendmail('[email protected]',email,msg.as_string()) # 我的邮箱,要发送的邮箱(引入参数),邮件的内容
server.quit()
- B同事调用A的函数
def execute():
try:
send_email('[email protected]','测试邮件')
except EmailValidError as e:
print(e.title)
except ContenRequireError as e:
print(e.title)
except Exception as e:
print(e)
execute()
- 定义好框架,遇到不同错误触发不同异常
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)
-
- 按照规定去触发指定异常,每种异常都具备特殊的含义
3.3 finally和else
3.3.1 特殊的finally
try:
# 逻辑代码
except Exception as e:
# try中的代码如果有异常,则次代码块中的代码会执行
finally:
# try中的代码无论是否报错都会执行,一般用于释放资源
print('end')
- 如果在函数中定义了
try
和except
,不管里面是否出现了return
,finally
一定会在return之前
执行
def func(name):
try:
return name
except Exception as e:
pass
finally:
print('123')
func('小明')
- 不建议在finally里面写return语句,这样会使其他地方的return语句都失效
def func(name):
try:
return name
except Exception as e:
pass
finally:
return '123'
v1 = func('小明')
print(v1) # 123
3.3.2 else
- else存在的意义在于判断try语句有没有异常,有异常则else不会执行 。
try:
print('执行sql')
错误的sql
except Exception as e:
print('回滚')
else:
print('提交sql')
3.3.3 traceback
- 异常回溯
import traceback
try:
a=1/0
except:
traceback.print_exc()
finally:
print("有无异常都执行一次")
3.4 异常练习题
- 实现捕获程序中的异常
class IterRange(object):
def __init__(self, num):
self.num = num
self.counter = -1
def __iter__(self):
return self
def __next__(self):
self.counter += 1
if self.counter == self.num:
raise StopIteration()
return self.counter
obj = IterRange(20)
# 补充代码
while True:
try:
ele = next(obj)
except StopIteration as e:
print("数据捕获完毕")
break
print(ele)
- 实现捕获程序中的异常
class IterRange(object):
def __init__(self,num):
self.num = num
self.counter = -1
def __iter__(self):
return self
def __next__(self):
self.counter += 1
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 IterRange(self.max_num)
data_object = Xrange(100) # 实例化出一个可迭代对象
obj_iter = data_object.__iter__()
# 补充代码
while True:
try:
ele = next(obj_iter)
except StopIteration as e:
print("数据捕获完毕")
break
print(ele)
- 实现捕获程序中的错误
def func():
yield 1
yield 2
yield 3
gen = func()
# 补充代码
while True:
try:
ele = next(gen)
except StopIteration as e:
print("数据捕获完毕")
break
print(ele)
- 实现捕获程序中的错误
# invalid literal for int() with base 10: '小明'
try:
name = int("小明")
except ValueError as e:
print(e)
# list index out of range
data = [11,22,33,44,55]
try:
data[1000]
except IndexError as e:
print(e)
# KeyError: 'xxx'
data = {'k1': 123, 'k2': 456}
try:
data['xxx']
except KeyError as e:
print('没有Key:',e)
- 分析代码写结果
class MyDict(dict):
def __getitem__(self, item):
try:
return super().__getitem__(item)
except KeyError as e:
return None
info = MyDict()
info['name'] = '小明'
info['age'] = 18
print(info['name']) # 小明 info['name'] -> __getitem__
print(info['email']) # None info['name'] -> __getitem__
- 看代码写结果
def func():
print('666')
return "成功"
def run(handler):
try:
num = handler()
print(num)
return func()
except Exception as e:
return "错误"
finally:
print("End")
print("结束")
res = run(lambda :123) # run传入了一个匿名函数的返回值 (lambda :123 匿名函数返回值123
print(res)
"""
return 123
print('666')
print("End")
return "成功"
"""
4. 反射
反射的关键特点是以字符串的形式操作对象,它提供了动态性,允许你在运行时基于条件和变量来访问对象的属性和方法。
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
def show(self):
print("姓名:{}\t年龄:{}".format(self.name, self.age))
user_obj = Person('小明', 18)
# 对象.成员的格式去获取数据
print(user_obj.name, user_obj.age)
user_obj.show()
# 对象.成员的格式去设置数据
user_obj.name = '小红'
当你使用 method = user_obj.name
这种方式时,它是静态的,方法名称是固定的,不具备动态性,只能访问特定方法
而使用 method = getattr(obj, "name")
这种方式是反射,你可以通过字符串变量来动态选择要调用的方法,这使得代码更加灵活和通用。
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
def show(self):
print("姓名:{}\t年龄:{}".format(self.name, self.age))
user_obj = Person('小明', 18)
# getattr 获取成员
getattr(user_obj, 'name')
getattr(user_obj, 'age')
getattr(user_obj, 'show')() # user_obj.show()
method = getattr(user_obj, 'show')
method()
# setattr 设置成员
setattr(user_obj, 'name', '小红') # user_obj.name = '小红'
getattr(user_obj, 'show')() # user_obj.show()
Python中提供了4个内置函数来提供反射:
getattr
:去对象中获取成员
v1 = getattr(对象,"成员名称") # 如果成员不存在则返回None
v2 = getattr(对象,"成员名称", "不存在时的默认值")
setattr
:去对象中设置成员
setattr(对象,"成员名称",值)
hasattr
:判断对象中是否包含成员
v1 = hasattr(对象,"成员名称") # True/False
delattr
:删除对象中的成员
delattr(对象,"成员名称")
delattr(user_obj,"name") # del user_obj.name
案例:
class Account(object):
def login(self):
print("请注册")
def register(self):
print("请注册")
def index(self):
print("欢迎!")
def run():
name = input("输入要执行的方法:") # login、register、index、run、xxx
account_object = Account()
method = getattr(account_object, name, None)
if not method:
print("没有这个方法")
return
method()
run()
4.1 一切皆对象
在Python中:一起皆对象
,每个对象的内部都有自己维护的成员
- 对象是对象
- 类是对象(平时不这么称呼)
- 模块是对象(平时不这么称呼)
- ...
由于反射支持以字符串的形式去对象中操作成员 等价于 对象.成员
,所以基于反射,也可以对 类
、模块
中的成员进行操作
只要看到 xx.xx 都可以用反射实现
class Person(object):
title = "嘿嘿"
v1 = Person.title
print(v1)
v2 = getattr(Person,'title')
print(v2)
import re
email = re.match('\w.+@\w.+\.com','[email protected]')
# print(email.group())
get_email = getattr(email,'group')
print(get_email())
func = getattr(re,'match')
get_email2 = func('\w.+@\w.+\.com','[email protected]')
print(get_email2.group())
4.2 import_module + 反射
在Python中导入一个模块可以通过import
导入,也可以通过字符串的形式导入
-
注意:import_module最大只能导入模块级别,无法导入模块内的成员
-
示例1:
# 导入模块
# import random as m
# print(m.randint(1,100))
from importlib import import_module
m = import_module('random')
print(m.randint(1,100))
- 示例2:
# 导入模块 exceptions
# from requests import exceptions as m
from importlib import import_module
m = import_module('requests.exceptions')
- 示例3:
# 导入模块exceptions,获取exceptions中的InvalidURL类
# from requests.exceptions import InvalidURL as m
from importlib import import_module
# 错误方式
# m = import_module('requests.exceptions.InvalidURL') # 报错,import_module 只能导入到模块级别
# 正确方式
m = import_module('requests.exceptions') # 先导入模块,m 代表导入成功后模块的名字
cls = m.InvalidURL # 再去模块中获取类
在很多项目的源码中都有 import_module
和 getattr
配合实现字符串的形式导入模块并获取成员,例如:
反射.zip
from importlib import import_module
path = "openpyxl.utils.exceptions.InvalidFileException"
module_path,class_name = path.rsplit('.',maxsplit=1) # "openpyxl.utils.exceptions" "InvalidFileException"
module_obj = import_module(module_path) # "InvalidFileException"
cls = getattr(module_obj,class_name)
print(cls) # <class 'openpyxl.utils.exceptions.InvalidFileException'>
5. 练习题
- super的作用
根据类继承关系向上寻找成员
- 看代码写结果
class Foo(object):
def __init__(self,name,age):
self.name = name
self.age = age
@property
def message(self):
return "{}-{}".format(self.name,self.age)
class Bar(Foo):
def __init__(self,email,*args,**kwargs):
super().__init__(*args,**kwargs)
self.email = email
def total(self):
data = "{}-{}-{}".format(self.name,self.age,self.email)
return data
obj1 = Foo("小明",18)
print(obj1.message) # 小明-18
obj2 = Bar("[email protected]",'root','100')
print(obj2.message) # root-100
print(obj2.total()) # [email protected]
- 看代码写结果
class Foo(object):
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)
- 如何主动触发一个异常
raise 异常类(...)
- 反射的作用
根据字符串的形式操作成员
- 看代码写结果
class Person(object):
title = "北京"
def __init__(self, name, age):
self.name = name
self.age = age
def show(self):
msg = "姓名:{}\t年龄:{}".format(self.name, self.age)
return msg
@property
def msg(self):
return 666
@staticmethod
def something():
return 99
obj = Person('小明', 18)
print(getattr(obj, 'age')) # 18
print(getattr(obj, 'msg')) # 666
print(getattr(obj, 'show')()) # 姓名:小明
print(getattr(obj, 'something')()) # 99
标签:__,python,self,基础,面向对象,mro,pass,print,class
From: https://www.cnblogs.com/ican97/p/17777483.html