python面向对象(3)

简介: python面向对象(3)

面向对象高级和应用

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

概要

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

1. 继承【补充】

对于Python面向对象中的继承,我们已学过:

  • 继承存在意义:将公共的方法提取到父类中,有利于增加代码重用性。
  • 继承的编写方式:
# 继承
class Base(object):
    pass
class Foo(Base):
    pass
# 多继承
class Base(object):
    pass
class Bar(object):
    pass
class Foo(Base,Bar):
    pass
  • 调用类中的成员时,遵循:
  • 优先在自己所在类中找,没有的话则去父类中找。
  • 如果类存在多继承(多个父类),则先找左边再找右边。

上述的知识点掌握之后,其实就可以解决继承相关的大部分问题。

但如果遇到一些特殊情况(不常见),你就可能不知道怎么搞了,例如:

96447814-120fc980-1245-11eb-938d-6ea408716c72.png

96447814-120fc980-1245-11eb-938d-6ea408716c72.png

96447814-120fc980-1245-11eb-938d-6ea408716c72.png

1.1 mro和c3算法

如果类中存在继承关系,在可以通过mro()获取当前类的继承关系(找成员的顺序)。

96447814-120fc980-1245-11eb-938d-6ea408716c72.png

示例1:

mro(A) = [A] + [B,C]
mro(A) = [A,B,C]
mro(A) = [A] + merge( mro(B), mro(C), [B,C] )
mro(A) = [A] + merge( [object], [object], [] )
mro(A) = [A] + [B,C,object]
mro(A) = [A,B,C,object]
mro(A) = [A] + merge( mro(B), mro(C), [B,C] )
mro(A) = [A] + merge( [], [C], [,C] 
mro(A) = [A] + [B,C]
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:

96447814-120fc980-1245-11eb-938d-6ea408716c72.png

mro(A) = [A] + merge( mro(B), mro(C), [B,C] )
mro(A) = [A] + merge( [], [D], [] )
mro(A) = [A] + [B,C,D]
mro(A) = [A,B,C,D]
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:

96447814-120fc980-1245-11eb-938d-6ea408716c72.png

mro(A) = [A] + merge( mro(B),mro(C),[B,C])
mro(A) = [A] + merge( [], [C], [C])
mro(A) = [A,B,D,C]
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:

96447814-120fc980-1245-11eb-938d-6ea408716c72.png

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] 
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:

96447814-120fc980-1245-11eb-938d-6ea408716c72.png

简写为:A -> B -> D -> G -> H -> K -> C -> E -> F -> M -> N -> P -> object
mro(A) = [A] + merge( mro(B),          mro(C),      mro(P),      [B,C,P])
                  []   [N]     [P]          [P]
mro(A) = [A,B,D,G,H,K,C,E,F,M,N,P]
-----------------------------------------------------
mro(B) = [B] + merge( mro(D), mro(E), [D,E])
mro(D) = [D] + merge(mro(G),mro(H), [G,H])
mro(G) = [G]
mro(H) = [H,K]
mro(B) = [B] + merge( [], [E,M], [E])
mro(B) = [B,D,G,H,K,E,M]
-----------------------------------------------------
mro(C) = [C] + merge(mro(E),mro(F),[E,F])
mro(E) = [E,M]
mro(F) = [F,M,N] 
mro(C) = [C] + merge([M],[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()) # 简写为:A -> B -> D -> G -> H -> K -> C -> E -> F -> M -> N -> P -> object

特别补充:一句话搞定继承关系

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

所以,我自己根据经验总结了一句话赠送给大家:从左到右,深度优先,大小钻石,留住顶端,基于这句话可以更快的找到继承关系。

96447814-120fc980-1245-11eb-938d-6ea408716c72.png

简写为: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之后 中就出现了经典类和新式类共存。(正式支持是2.3)
  • 最终,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类型。【从左到右,深度优先,大小钻石,不留顶端】

class Foo:
    pass

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

class Foo(object):
    pass

class Base(object):
    pass
class Foo(Base):
    pass

Py3


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

class Foo:
    pass
class Bar(object):
    pass

2. 内置函数补充

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

  • classmethod、staticmethod、property 。
  • 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

所以当你以后在见到下面的情况时,首先就要想到handler可以是:函数、类、具有call方法的对象 这三种,到底具体是什么,需要根据代码的调用关系才能分析出来。

def do_something(handler):
    handler()

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'] = "lbw"
info["age"] = 18
value = info.get("age")
print(value)
class MyDict(dict):
    def get(self, k):
        print("自定义功能")
        return super().get(k)
info = MyDict()
info['name'] = "lbw" # __setitem__
info["age"] = 18       # __setitem__
print(info)
value = info.get("age")
print(value)

96447814-120fc980-1245-11eb-938d-6ea408716c72.png

type,获取一个对象的类型。

v1 = "lbw"
result = type(v1)
print(result) # <class 'str'>
v2 = "lbw"
print( type(v2) == str )  # True
v3 = [11, 22, 33] # list(...)
print( type(v3) == list )  # True
class Foo(object):
    pass
v4 = Foo()
print( type(v4) == 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子类的实例。
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

issubclass,判断类是否是某个类的子孙类。

class Top(object):
    pass
class Base(Top):
    pass
class Foo(Base):
    pass
print(issubclass(Foo, Base))  # True
print(issubclass(Foo, Top))   # True

3.异常处理

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

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

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

try:
    res = requests.get(url=url)
except Exception as e:
    代码块,上述代码出异常待执行。
print("结束")
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)
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中的代码都会执行,一般用于释放资源。
print("end")
"""
try:
    file_object = open("xxx.log")
    # ....
except Exception as e:
    # 异常处理
finally:
    file_object.close()  # try中没异常,最后执行finally关闭文件;try有异常,执行except中的逻辑,最后再执行finally关闭文件。
"""    

3.1 异常细分

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)

之前只是简单的捕获了异常,出现异常则统一提示信息即可。如果想要对异常进行更加细致的异常处理,则可以这样来做:

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捕获错误就可以了。

如果想要对错误进行细分的处理,例如:发生Key错误和发生Value错误分开处理。

基本格式:

try:
    # 逻辑代码
    pass
except KeyError as e:
    # 小兵,只捕获try代码中发现了键不存在的异常,例如:去字典 info_dict["n1"] 中获取数据时,键不存在。
    print("KeyError")
except ValueError as e:
    # 小兵,只捕获try代码中发现了值相关错误,例如:把字符串转整型 int("无诶器")
    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月前
|
Java 程序员 C++
Python 面向对象详解!
本文详细介绍了Python中的面向对象编程(OOP),包括类、对象、继承、封装、多态和抽象等核心概念。通过具体示例,解释了如何使用类定义对象的属性和方法,以及如何通过继承实现代码重用。文章还探讨了封装和多态的重要性,并介绍了私有属性和抽象类的使用方法。最后,总结了OOP的四大支柱:封装、抽象、继承和多态,强调了这些概念在Python编程中的应用。适合Java程序员扩展Python编程知识。
72 2
|
1月前
|
Python
Python面向对象(2)
【10月更文挑战第14天】
Python面向对象(2)
|
3月前
|
Python
你真的会面向对象吗!解密Python“魔术方法”
你真的会面向对象吗!解密Python“魔术方法”
40 0
|
1月前
|
设计模式 程序员 C语言
Python面向对象
【10月更文挑战第13天】
Python面向对象
|
5月前
|
Python
Python进阶第一篇(Python的面向对象)
Python进阶第一篇(Python的面向对象)
|
6月前
|
存储 算法 安全
Python编程实验六:面向对象应用
Python编程实验六:面向对象应用
110 1
|
2月前
|
前端开发 Python
Python编程的面向对象有哪些(二)
Python编程的面向对象(二)—类的多态
|
2月前
|
IDE Java 开发工具
Python类与面向对象
Python类与面向对象
|
1月前
|
Python
Python编程-关于面向对象的一些
Python编程-关于面向对象的一些
|
6月前
|
人工智能 自然语言处理 开发者
Python基础教程——面向对象
Python基础教程——面向对象
下一篇
无影云桌面