轩小陌的Python笔记-day19 面向对象高级和应用

day19 面向对象高级和应用

在这里插入图片描述

课程目标:掌握面向对象高级知识和相关应用。

今日概要

  • 继承【补充】
  • 内置函数【补充】
  • 异常处理
  • 反射

1. 继承【补充】

对于 Python 面向对象中的继承,之前已讲解过:

  • 继承存在意义:将公共的方法提取到父类中,有利于增加代码重用性。

  • 继承的编写方式:

    # 继承
    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()获取当前类的继承关系(决定寻找成员的顺序)。

示例1:

在这里插入图片描述

# C3算法过程:
mro(A) = [A] + merge( mro(B), mro(C), [B,C] )
mro(A) = [A] + merge( [B,object], [C,object], [B,C] )
mro(A) = [A] + [B,C,object]
mro(A) = [A,B,C,object]
# C3算法过程(将object先提取出来):
mro(A) = [A] + merge( mro(B), mro(C), [B,C] )
mro(A) = [A] + merge( [B], [C], [B,C] + [object]
mro(A) = [A] + [B,C] + [object]
mro(A) = [A,B,C,object]                     
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'>
)

示例2:

在这里插入图片描述

# C3算法过程(将object先提取出来):
mro(A) = [A] + merge( mro(B), mro(C), [B,C] )
mro(A) = [A] + merge( [B], [C,D], [B,C] ) + [object]
mro(A) = [A] + merge( [], [C,D], [C] ) + [object]
mro(A) = [A] + merge( [], [D], [] ) + [object]
mro(A) = [A] + [B,C,D] + [object]
mro(A) = [A,B,C,D,object]
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'>
]

示例3:

在这里插入图片描述

# C3算法过程(将object先提取出来):
mro(A) = [A] + merge( mro(B), mro(C), [B,C] )
mro(A) = [A] + merge( [B,D], [C], [B,C] ) + [object]
mro(A) = [A] + merge( [D], [C], [C] ) + [object]
mro(A) = [A] + merge( [], [C], [C]) + [object]
mro(A) = [A] + [B,D,C] + [object]
mro(A) = [A,B,D,C,object]
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'>
]

示例4:

在这里插入图片描述

# C3算法过程(将object先提取出来):
mro(A) = [A] + merge( mro(B), mro(C), [B,C])
mro(A) = [A] + merge( [B,D], [C,D], [B,C]) + [object]
mro(A) = [A] + merge( [D], [C,D], [C]) + [object]
mro(A) = [A] + merge( [D], [D], []) + [object]
mro(A) = [A] + [B,C,D] + [object] 
mro(A) = [A,B,C,D,object] 
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'>
]

示例5:

在这里插入图片描述

简写为:A -> B -> D -> G -> H -> K -> C -> E -> F -> M -> N -> P -> object
# C3算法过程(将object先提取出来):
mro(A) = [A] + merge( mro(B), mro(C), mro(P), [B,C,P] )
mro(A) = [A] + merge( [B,D,G,H,K,E,M], [C,E,F,M,N], [P], [B,C,P] ) + [object]
mro(A) = [A] + merge( [D,G,H,K,E,M], [C,E,F,M,N], [P], [C,P] ) + [object]
mro(A) = [A] + merge( [G,H,K,E,M], [C,E,F,M,N], [P], [C,P] ) + [object]
mro(A) = [A] + merge( [H,K,E,M], [C,E,F,M,N], [P], [C,P] ) + [object]
mro(A) = [A] + merge( [K,E,M], [C,E,F,M,N], [P], [C,P] ) + [object]
mro(A) = [A] + merge( [E,M], [C,E,F,M,N], [P], [C,P] ) + [object]
mro(A) = [A] + merge( [E,M], [E,F,M,N], [P], [P] ) + [object]
mro(A) = [A] + merge( [M], [F,M,N], [P], [P] ) + [object]
mro(A) = [A] + merge( [M], [M,N], [P], [P] ) + [object]
mro(A) = [A] + merge( [], [N], [P], [P] ) + [object]
mro(A) = [A] + merge( [], [], [P], [P] ) + [object]
mro(A) = [A] + [B,D,G,H,K,C,E,F,M,N,P] + [object]
mro(A) = [A,B,D,G,H,K,C,E,F,M,N,P,object]
-----------------------------------------------------
mro(B) = [B] + merge( mro(D), mro(E), [D,E])
mro(B) = [B] + merge( [D,G,H,K], [E,M], [D,E])
mro(B) = [B] + merge( [G,H,K], [E,M], [E])
mro(B) = [B] + merge( [H,K], [E,M], [E])
mro(B) = [B] + merge( [K], [E,M], [E])
mro(B) = [B] + merge( [], [E,M], [E])
mro(B) = [B] + merge( [], [M], [])
mro(B) = [B] + [D,G,H,K,E,M]
mro(B) = [B,D,G,H,K,E,M]
-----------------------------------------------------
mro(C) = [C] + merge( mro(E), mro(F), [E,F] )
mro(C) = [C] + merge( [E,M], [F,M,N], [E,F] )
mro(C) = [C] + merge( [M], [F,M,N], [F] )
mro(C) = [C] + merge( [M], [M,N], [] )
mro(C) = [C] + merge( [], [N], [] )
mro(C) = [C] + [E,F,M,N]
mro(C) = [C,E,F,M,N]
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()) 
>>输出结果:
[
    <class '__main__.A'>, 
    <class '__main__.B'>, 
    <class '__main__.D'>, 
    <class '__main__.G'>, 
    <class '__main__.H'>, 
    <class '__main__.K'>, 
    <class '__main__.C'>, 
    <class '__main__.E'>, 
    <class '__main__.F'>, 
    <class '__main__.M'>, 
    <class '__main__.N'>, 
    <class '__main__.P'>, 
    <class 'object'>
]
小结:一句话搞定继承关系

如果用正经的C3算法规则去分析一个类继承关系有点繁琐,尤其是遇到一个复杂的类也要分析很久。

其实,对于继承关系,可以总结出一句话:从左到右,深度优先,大小钻石,留住顶端,基于这句话可以更快的找到继承关系。

在这里插入图片描述

A -> B -> D -> G -> H -> K -> C -> E -> F -> M -> N -> P -> object

1.2 继承关系在 py2 和 py3 的区别(了解)

概述:

  • 在 python2.2 之前,只支持经典类【从左到右,深度优先,大小钻石,不留顶端】

  • 后来,Python 想让类默认继承object(其他语言的面向对象基本上也都是默认都继承object),此时发现原来的经典类不能直接集成这个功能,有Bug。

  • 所以,Python决定不在原来的经典类上进行修改,而是再创建一个新式类来支持这个功能。【从左到右,深度优先,大小钻石,留住顶端。】

    • 经典类,不继承object类型

      class Foo:
          pass
      
    • 新式类,直接或间接继承object

      class Base(object):
          pass
      
      class Foo(Base):
          pass
      
  • 这样,在 python2.2 之后就出现了经典类和新式类共存。

  • 最终,在 python3 后便丢弃了经典类,只保留新式类。

详细文档:
	https://www.python.org/dev/peps/pep-0253/#mro-method-resolution-order-the-lookup-rule
	https://www.python.org/download/releases/2.3/mro/

In classic Python, the rule is given by the following recursive function, also known as the left-to-right depth-first rule.

def classic_lookup(cls, name):
    if cls.__dict__.has_key(name):
        return cls.__dict__[name]
    for base in cls.__bases__:
        try:
            return classic_lookup(base, name)
        except AttributeError:
            pass
    raise AttributeError, name
    
The problem with this becomes apparent when we consider a "diamond diagram":

      class A:
        ^ ^  def save(self): ...
       /   \
      /     \
     /       \
    /         \
class B     class C:
    ^         ^  def save(self): ...
     \       /
      \     /
       \   /
        \ /
      class D
      

Arrows point from a subtype to its base type(s). This particular diagram means B and C derive from A, and D derives from B and C (and hence also, indirectly, from A).

Assume that C overrides the method save(), which is defined in the base A. (C.save() probably calls A.save() and then saves some of its own state.) B and D don't override save(). When we invoke save() on a D instance, which method is called? According to the classic lookup rule, A.save() is called, ignoring C.save()!

This is not good. It probably breaks C (its state doesn't get saved), defeating the whole purpose of inheriting from C in the first place.

Why was this not a problem in classic Python? Diamond diagrams are rarely found in classic Python class hierarchies. Most class hierarchies use single inheritance, and multiple inheritance is usually confined to mix-in classes. In fact, the problem shown here is probably the reason why multiple inheritance is unpopular in classic Python.

Why will this be a problem in the new system? The 'object' type at the top of the type hierarchy defines a number of methods that can usefully be extended by subtypes, for example __getattr__().

(Aside: in classic Python, the __getattr__() method is not really the implementation for the get-attribute operation; it is a hook that only gets invoked when an attribute cannot be found by normal means. This has often been cited as a shortcoming -- some class designs have a legitimate need for a __getattr__() method that gets called for all attribute references. But then of course this method has to be able to invoke the default implementation directly. The most natural way is to make the default implementation available as object.__getattr__(self, name).)

Thus, a classic class hierarchy like this:

class B     class C:
    ^         ^  def __getattr__(self, name): ...
     \       /
      \     /
       \   /
        \ /
      class D
      

will change into a diamond diagram under the new system:

      object:
        ^ ^  __getattr__()
       /   \
      /     \
     /       \
    /         \
class B     class C:
    ^         ^  def __getattr__(self, name): ...
     \       /
      \     /
       \   /
        \ /
      class D


and while in the original diagram C.__getattr__() is invoked, under the new system with the classic lookup rule, object.__getattr__() would be invoked!

Fortunately, there's a lookup rule that's better. It's a bit difficult to explain, but it does the right thing in the diamond diagram, and it is the same as the classic lookup rule when there are no diamonds in the inheritance graph (when it is a tree).

小结:Python2和Python3在关于面向对象的区别。

  • Py2:

    • 经典类,未继承object类型。【从左到右,深度优先,大小钻石,不留顶端】

    • 新式类,直接获取间接继承object类型。【从左到右,深度优先,大小钻石,留住顶端 – C3算法】

  • Py3

    • 新式类,丢弃了经典类只保留了新式类。【从左到右,深度优先,大小钻石,留住顶端 – C3算法】

2. 内置函数补充

本次要给讲解的内置函数共8个,都跟面向对象的知识相关。

2.1 classmethod、staticmethod、property (上节已讲)
2.2 callable:是否可在后面加括号执行。
  • 函数

    def func():
        pass
    
    print( callable(func) ) 
    >>输出结果:True
    
  • class Foo(object):
        pass
    
    print( callable(Foo) ) 
    >>输出结果:True
    
  • 类中具有__call__方法的对象

    class Foo(object):
    	pass
    
    obj = Foo()
    print( callable(obj) ) 
    >>输出结果:False
    
    class Foo(object):
    
        def __call__(self, *args, **kwargs):
            pass
        
    obj = Foo()
    print( callable(obj) ) 
    >>输出结果:True
    

    了解了以上3种情况,在以后在遇到下面的情况时,那么 handler 可能是:函数、类、具有call方法的对象,具体是什么,需要分析代码的调用关系才能最终确定:

    def do_something(handler):
        handler()
    
2.3 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)


obj = Foo()
obj.message(1)

>>输出结果:
Foo.message 1
Top.message 101
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()
obj.message(1)

>>> Base.message 1
>>> Bar.message 1000

应用场景

假设有一个类,他原来已实现了某些功能,现在想在他的基础上再扩展点功能,但重新写一遍又比较麻烦,此时可以用super。

info = dict() 
info['name'] = "轩小陌"
info["age"] = 18
value = info.get("age")
print(value)
>>输出结果:
18
class MyDict(dict):

    def get(self, k):
        print("自定义功能")
        return super().get(k)

info = MyDict()
info['name'] = "轩小陌" 
info["age"] = 18   
value = info.get("age")
print(value)
>>输出结果:
自定义功能
18

在这里插入图片描述

2.4 type:获取一个对象的类型。
v1 = "轩小陌"
result = type(v1)
print(result) 
>>输出结果:
<class 'str'>
v2 = "轩小陌"
print( type(v2) == str )  
>>输出结果:True

v3 = [11, 22, 33]
print( type(v3) == list )  
>>输出结果:True
class Foo(object):
    pass

v4 = Foo()
print( type(v4) == Foo )  
>>输出结果:True
2.5 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子类的实例对象】

示例:

class Animal(object):
    def run(self):
        pass

class Dog(Animal):
    pass

class Cat(Animal):
    pass

data_list = [
    "alex",
    Dog(),
    Cat(),
	"root"
]

for item in data_list:
    if type(item) == Cat:
        item.run()
    elif type(item) == Dog:
        item.run()
    else:
        pass
    
for item in data_list:
    if isinstance(item, Animal):
        item.run()
    else:
        pass
2.6 issubclass:判断类是否是某个类的子类。
class Top(object):
    pass

class Base(Top):
    pass

class Foo(Base):
    pass

print(issubclass(Foo, Base))  
>>输出结果:True
print(issubclass(Foo, Top))  
>>输出结果:True

3.异常处理

在程序开发中如果遇到一些 不可预知的错误或懒得做一些判断时,可以选择用异常处理来做。

示例1:

import requests

while True:
    url = input("请输入要下载网页地址:")
    res = requests.get(url=url)
    with open('content.txt', mode='wb') as f:
        f.write(res.content)

上述下载视频的代码在正常情况下可以运行,但如果遇到网络出问题,那么此时程序就会报错无法正常执行。

import requests

while True:
    url = input("请输入要下载网页地址:")
    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)

示例2:

num1 = input("请输入数字:")
num2 = input("请输入数字:")
try:
    num1 = int(num1)
    num2 = int(num2)
    result = num1 + num2
    print(result)
except Exception as e:
    print("输入错误")

异常处理常见的应用场景:

  • 调用微信的 API 实现微信消息的推送、微信支付等

  • 支付宝支付、视频播放等

  • 数据库或 redis 连接和操作

  • 调用第三方的视频播放发的功能,由第三方的程序出问题导致的错误。

异常处理的基本格式:

try:
    # 逻辑代码
except Exception as e:
    # try中的代码如果有异常,则此代码块中的代码会执行。
try:
    # 逻辑代码
except Exception as e:
    # try中的代码如果有异常,则此代码块中的代码会执行。
finally:
    # try中的代码无论是否报错,finally中的代码都会执行,一般用于释放资源。

示例:

try:
    file_object = open("xxx.log")
    ...
except Exception as e:
    ...
finally:
    file_object.close()  
    # 如果try中代码块没异常,则正常执行,最后执行finally关闭文件;如果try中代码块有异常,则执行except中的代码块,最后再执行finally关闭文件。 

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:
    # 只捕获try代码中发现了键不存在的异常
    print("KeyError")

except ValueError as e:
    # 只捕获try代码中发现了值相关错误
    print("ValueError")

except Exception as e:
    # 处理上面except捕获不了的错误(可以捕获所有的错误)。
    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


try:
    pass
except MyException as e:
    print("MyException异常被触发了", e)
except Exception as e:
    print("Exception", e)

上述代码在except中定义了捕获MyException异常,但他永远不会被触发。因为默认的异常都有特定的触发条件,例如:索引不存在、键不存在会触发 IndexError 和 KeyError 异常。

那么,对于我们自定义的异常,怎么才能触发呢?需要使用:raise MyException()类实现:

class MyException(Exception):
    pass


try:
    ...
    raise MyException()
    ...
except MyException as e:
    print("MyException异常被触发了", e)
except Exception as e:
    print("Exception", e)
class MyException(Exception):
    def __init__(self, msg, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.msg = msg


try:
    raise MyException("xxx失败了")
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)

案例1:两个人合作协同开发,A调用B写的方法:

  • A定义了一个函数

    class EmailValidError(Exception):
        title = "邮箱格式错误"
    
    class ContentRequiredError(Exception):
        title = "文本不能为空错误"
        
    def send_email(email,content):
        if not re.match("\w+@live.com",email):
            raise EmailValidError()
    	if len(content) == 0 :
            raise ContentRequiredError()
    	...
    
  • B调用A写的函数

    def execute():
        ...
        
    	try:
            send_email(...)
        except EmailValidError as e:
            pass
        except ContentRequiredError as e:
            pass
        except Exception as e:
            print("发送失败")
    
    execute()
    

案例2:在框架内部已经定义好,遇到不同的错误会触发不同的异常:

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.4 finally 的特殊性

在 try 或 except 的代码块中如果定义了return,也会执行最后的finally块中的代码。如:

def func():
    try:
        return 123
    except Exception as e:
        pass
    finally:
        print(666)
        
result = func()
print(result)
>>输出结果:
666
123

练习题

  1. 补充代码实现捕获程序中的错误。

    # 迭代器
    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)
    
  2. 补充代码实现捕获程序中的错误。

    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)
    
  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]
    data[1000]
    
    try:
        data = [11,22,33,44,55]
    	data[1000]
    except IndexError as e:
        print("转换失败")
    
  6. 补充代码实现捕获程序中的错误。(注意:本案例仅用于练习,在真实开发中对于这种情况建议还是自行做判断处理,不要用异常)

    data = {"k1":123,"k2":456}
    data["xxxx"]
    
    try:
        data = {"k1":123,"k2":456}
    	data["xxxx"]
    except KeyError as e:
        print("转换失败")
    
  7. 分析代码,写结果

    class MyDict(dict):
    
        def __getitem__(self, item):
            try:
                return super().__getitem__(item) # KeyError
            except KeyError as e:
                return None
    
    
    info = MyDict()
    info['name'] = "轩小陌"
    info['wx'] = "xuanxiaom666"
    
    print(info['wx'])     
    print(info['email'])
    >>输出结果:
    xuanxiaom666
    None
    
  8. 看代码写结果

    def run(handler):
        try:
            num = handler()
            print(num)
            return "成功"
        except Exception as e:
            return "错误"
        finally:
            print("END")
    
        print("结束")
    
    res = run(lambda: 123) 
    print(res)
    >>输出结果:
    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")
    
        print("结束")
    
    res = run(lambda: 123) 
    print(res)
    >>输出结果:
    123
    666
    END
    成功
    

4.反射

4.1 反射的定义

反射,提供了一种更加灵活的方式实现从对象中操作成员(以字符串的形式在对象中进行成员的操作)。

class Person(object):
    
    def __init__(self,name,wx):
        self.name = name
        self.wx = wx
	
    def show(self):
        message = "姓名{},微信:{}".format(self.name,self.wx)
        
user_object = Person("轩小陌","xuanxiaomo666")

常规方式:

# 通过"对象.成员"获取变量、方法
user_object.name
user_object.wx
user_object.show()

# 通过"对象.成员"设置成员
user_object.name = "轩小陌小轩"

反射方式:

# getattr 获取变量
getattr(user,"name") 				# user.name
getattr(user,"wx")   				# user.wx

# getattr 获取方法
method = getattr(user,"show") 		# user.show
method()
getattr(user,"show")()

# setattr 设置成员
setattr(user, "name", "轩小陌") 	  # user.name = "轩小陌"

Python中有4个内置函数支持反射:

  • getattr,去对象中获取成员

    v1 = getattr(对象,"成员名称")
    v2 = getattr(对象,"成员名称", 不存在时的默认值)
    
  • setattr,去对象中设置成员

    setattr(对象,"成员名称",值)
    
  • hasattr,对象中是否包含成员

    v1 = hasattr(对象,"成员名称") 
    
  • delattr,删除对象中的成员

    delattr(对象,"成员名称")
    

后续再遇到 “对象.成员” 获取成员和设置成员的情况时,均可以基于反射来实现。

案例:

class Account(object):

    def login(self):
        pass

    def register(self):
        pass

    def index(self):
        pass

    
def run(self):
    name = input("请输入要执行的方法名称:") 
    
    account_object = Account()
    method = getattr(account_object, name, None) 
    
    if not method:
        print("输入错误")
        return 
    method()

4.1 一切皆对象

在Python中有一条准则:一切皆对象。 每个对象的内部都有自己维护的成员。

  • 对象是对象

    class Person(object):
        
        def __init__(self,name,wx):
            self.name = name
            self.wx = wx
    	
        def show(self):
            message = "姓名{},微信:{}".format(self.name,self.wx)
            
            
    user_object = Person("轩小陌","xuanxiaomo666")
    user_object.name
    
  • 类是对象

    class Person(object):
        title = "轩小陌"
    
    Person.title
    
  • 模块是对象

    import re
    
    re.match
    

由于反射支持以字符串的形式去对象中操作成员,所以,基于反射也可以对类、模块中的成员进行操作:

class Person(object):
    title = "轩小陌"

v1 = Person.title
print(v1)
v2 = getattr(Person,"title")
print(v2)
import re

v1 = re.match("\w+","dfjksdufjksd")
print(v1)

func = getattr(re,"match")
v2 = func("\w+","dfjksdufjksd")
print(v2)

总而言之,在面向对象中,所有的 “对象.成员” 的方式都可以通过反射来实现。

4.2 import_module + 反射

在 Python 中如果想要导入一个模块,可以通过 import 语法导入,也可以通过字符串的形式导入:

常规导入模块的方式与通过 import_module 导入模块的方式对比:

示例1:

# 常规方式:
import random

v1 = random.randint(1,100)
# import_module方式:
from importlib import import_module

m = import_module("random")

v1 = m.randint(1,100)

示例2:

# 常规方式:
from requests import exceptions as m
# import_module方式:
from importlib import import_module
m = import_module("requests.exceptions")

注意:import_module 最多只能导入到模块级别:

from requests.exceptions import InvalidURL
# 错误方式:
from importlib import import_module
m = import_module("requests.exceptions.InvalidURL")
# 正确方式:
from importlib import import_module
m = import_module("requests.exceptions")

cls = m.InvalidURL

在很多项目的源码中都会有 import_modulegetattr 配合实现根据字符串的形式导入模块并获取成员,例如:

from importlib import import_module

path = "openpyxl.utils.exceptions.InvalidFileException"

module_path,class_name = path.rsplit(".",maxsplit=1) 

module_object = import_module(module_path)

cls = getattr(module_object,class_name)

print(cls)

我们在开发中也可以基于 import_module 来进行开发,提高代码的可扩展性。

今日总结

  1. 了解 mro和c3算法。

  2. python2 和 python3 在面向对象中的区别。

  3. 内置函数:

    staticmethod,classmethod,property,callable,type,isinstance,issubclass,super
    getattr,setattr,hasattr,delattr
    
  4. 异常处理。

  5. 根据字符串的形式导入模块 import_module

  6. 根据字符串的形式操作成员 反射-getattr,setattr,hasattr,delattr

今日作业

  1. super的作用?

    安装mor继承关系向上找成员。
    
  2. 看图分析类A的继承关系
    在这里插入图片描述

    A->B->C->M->D->F->J->G->H
    
  3. 看代码写结果

    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("轩小陌", 20)
    print(obj1.message)
    >>输出结果:
    轩小陌-20
    
    obj2 = Bar("xx@live.com", "root", 100)
    print(obj2.message)
    print(obj2.total())
    >>输出结果:
    root-100
    root-100-xx@live.com
    
  4. 看代码写结果

    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)
    >>输出结果:
    轩小陌
    {}
    123
    True
    <__main__.Foo object at 0x0000016E6A61BC70>
    666
    
  5. 如何主动触发一个异常?

    raise 异常类(...)
    
  6. 反射的作用?

    可以通过以字符串的形式实现对象对成员的操作,或类、模块对成员的操作。
    
  7. 看代码写结果

    class Person(object):
        title = "北京"
    
        def __init__(self, name, wx):
            self.name = name
            self.wx = wx
    
        def show(self):
            message = "姓名{},微信:{}".format(self.name, self.wx)
            return message
    
        @property
        def message(self):
            return 666
    
        @staticmethod
        def something():
            return 999
    
    obj = Person("轩小陌", "xuanxiaomo666")
    
    print(getattr(obj, 'wx'))
    print(getattr(obj, 'message'))
    print(getattr(obj, 'show')()) 
    print(getattr(obj, 'something')())
    >>输出结果:
    xuanxiaomo666
    666
    姓名轩小陌,微信:xuanxiaomo666
    999
    
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值