PYTHON 面向对象

类的方法

  • 绑定方法,默认有一个self参数,由对象进行调用(此时self就等于调用方法的这个对象)【对象&类均可调用】
  • 类方法,默认有一个cls参数,用类或对象都可以调用(此时cls就等于调用方法的这个类)【对象&类均可调用】
  • 静态方法,无默认参数,用类和对象都可以调用。【对象&类均可调用】
    在Python中比较灵活,方法都可以通过对象和类进行调用;而在java、c#等语言中,绑定方法只能由对象调用;类方法或静态方法只能由类调用。
class Foo(object):

    def __init__(self, name,age):
        self.name = name
        self.age = age

    def f1(self):
        print("绑定方法", self.name)

    @classmethod
    def f2(cls):
        print("类方法", cls)

    @staticmethod
    def f3():
        print("静态方法")
        
# 绑定方法(对象)
obj = Foo("武沛齐",20)
obj.f1()
Foo.f1(obj)

# 类方法
Foo.f2()  # cls就是当前调用这个方法的类。(类)
obj.f2()  # cls就是当前调用这个方法的对象的类。


# 静态方法
Foo.f3()  # 类执行执行方法(类)
obj.f3()  # 对象执行执行方法

@classmethod 和 @staticmethod区别:后者相当于类中的普通函数,也不会默认传入cls的类对象

类的属性

属性其实是由绑定方法 + 特殊装饰器 组合创造出来的,让我们以后在调用方法时可以不加括号,例如:

class Foo(object):

    def __init__(self, name):
        self.name = name

    def f1(self):
        return 123

    @property
    def f2(self):
        return 123


obj = Foo("武沛齐")

v1 = obj.f1()
print(v1)
# ------------------------------------
class Pagination:
    def __init__(self, current_page, per_page_num=10):
        self.per_page_num = per_page_num

        if not current_page.isdecimal():
            self.current_page = 1
            return
        current_page = int(current_page)
        if current_page < 1:
            self.current_page = 1
            return
        self.current_page = current_page

    @property
    def start(self):
        return (self.current_page - 1) * self.per_page_num

    @property
    def end(self):
        return self.current_page * self.per_page_num


user_list = ["用户-{}".format(i) for i in range(1, 3000)]

# 分页显示,每页显示10条
while True:
    page = input("请输入页码:")

    pg_object = Pagination(page, 20)
    page_data_list = user_list[ pg_object.start : pg_object.end ]
    
    for item in page_data_list:
        print(item)

关于属性的编写有两种方式:

  • 方式一,基于装饰器

    class C(object):
        
        @property
        def x(self):
            pass
        
        @x.setter
        def x(self, value):
            pass
        
        @x.deleter
        def x(self):
    		pass
            
    obj = C()
    
    obj.x
    obj.x = 123
    del obj.x
    
  • 方式二,基于定义变量

    class C(object):
        
        def getx(self): 
    		pass
        
        def setx(self, value): 
    		pass
            
        def delx(self): 
    		pass
            
        x = property(getx, setx, delx, "I'm the 'x' property.")
        
    obj = C()
    
    obj.x
    obj.x = 123
    del obj.x
    

写在最后,对属性进行一个补充:

由于属性和实例变量的调用方式相同,所以在编写时需要注意:属性名称 不要 实例变量 重名。

class Foo(object):

    def __init__(self, name, age):
        self.name = name  # 报错,错认为你想要调用 @name.setter 装饰的方法。
        self.age = age

    @property
    def name(self):
        return "{}-{}".format(self.name, self.age)


obj = Foo("武sir", 123)

成员修饰符

Python中成员的修饰符就是指的是:公有、私有。
私有的范围可以是类内部的变量,方法,属性.

  • 公有,在任何地方都可以调用这个成员。
  • 私有,只有在类的内部才可以调用改成员(成员是以两个下划线开头,则表示该成员为私有)。父类中的私有成员,子类无法继承。

示例一:

class Foo(object):

    @property
    def __name(self):
        print("公有的get_age")

    @property
    def proxy(self):
        print("公有的proxy")
        self.__name
        return 1


obj = Foo()
v1 = obj.proxy
print(v1)
obj.__name() #私有方法,无法外部调用

示例二 父类中的私有成员,子类无法继承。

class Base(object):

    def __data(self):
        print("base.__data")

    def num(self):
        print("base.num")

	def data(self):
        self.__data() # 只能通过父类中的方法进行内部调用
        
class Foo(Base):

    def func(self):
        self.num()
        self.__data() # # 不允许执行父类中的私有方法


obj = Foo()
obj.func()

按理说私有成员是无法被外部调用,但如果用一些特殊的语法(_类名__方法名)也可以。

class Base(object):

    def __data(self):
        print("base.__data")

    def num(self):
        print("base.num")


obj = Base()
obj.__data()  # 无法调用私有方法
obj._Base__data() #通过_类名__方法的方式可以调用,但不推荐

元类metaclass

我们先来看类的创建方式有2种
第一种

class myclass:
    v1 = "v1"

    def func(self):
        print("开始")
        return 123

    def func2(self):
        return lambda self:4444

foo=myclass()
a=foo.func()
b=foo.func2()
print(a, b)

第二种 类的创建本质就是利用type创建的类,上面的类的创建模式底层原理也是这么创建的

def test():
    print("开始")
    return 123


# 类的创建本质就是利用type创建的类,上面的类的创建模式底层原理也是这么创建的
foo = type("myclass", (object,), {"v1": "v1", "func": test, "func2": lambda self: 4444})
a = foo.func()
b = foo.func2(foo)
print(a, b)

而第一种写法完整的写法是 class myclass(metaclass=type) metaclass是元类概念,是告诉解释器用哪个元类创建类,默认是type就是利用type创建的类

class MyType(type):
    def __new__(cls, *args, **kwargs):
        args[2]["one"]=1
        print("MyType创建类,name是%s,base是%s 属性方法是%s"%(args[0],args[1],args[2]))
        xx = super().__new__(cls,*args, **kwargs)
        return xx

#运行到这里的时候,
class Base(metaclass=MyType):
    age=19

class Foo(Base):
    pass

class INFO():
    pass

输出的内容是
MyType创建类,name是Base,base是() 属性方法是{'__module__': '__main__', '__qualname__': 'Base', 'age': 19, 'one': 1}
MyType创建类,name是Foo,base是(<class '__main__.Base'>,) 属性方法是{'__module__': '__main__', '__qualname__': 'Foo', 'one': 1}

从上面可以看到

  1. Base和Foo在类的生成时候会用元类的类执行__new__方法. 子类也会用父类的元类生成.
  2. __new__会传元祖,里面有三个参数,分别是类名,继承的父类,和里面的属性和方法
  3. args[2]["one"]=1 元类会再生成实例前给类创建属性

对象嵌套

在基于面向对象进行编程时,对象之间可以存在各种各样的关系,例如:组合、关联、依赖等(Java中的称呼),用大白话来说就是各种嵌套。
下面我们就用示例来学习常见的嵌套的情景:

情景一:

class Student(object):
    """ 学生类 """

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def message(self):
        data = "我是一名学生,我叫:{},我今年{}岁".format(self.name, self.age)
        print(data)

s1 = Student("武沛齐", 19)
s2 = Student("Alex", 19)
s3 = Student("日天", 19)



class Classes(object):
    """ 班级类 """

    def __init__(self, title):
        self.title = title
        self.student_list = []

    def add_student(self, stu_object):
        self.student_list.append(stu_object)

    def add_students(self, stu_object_list):
        for stu in stu_object_list:
            self.add_student(stu)

    def show_members(self):
        for item in self.student_list:
            # print(item)
            item.message()

c1 = Classes("三年二班")
c1.add_student(s1)
c1.add_students([s2, s3])

print(c1.title)
print(c1.student_list)

情景二:

class Student(object):
    """ 学生类 """

    def __init__(self, name, age, class_object):
        self.name = name
        self.age = age
        self.class_object = class_object

    def message(self):
        data = "我是一名{}班的学生,我叫:{},我今年{}岁".format(self.class_object.title, self.name, self.age)
        print(data)


class Classes(object):
    """ 班级类 """

    def __init__(self, title):
        self.title = title


c1 = Classes("Python全栈")
c2 = Classes("Linux云计算")

user_object_list = [
    Student("武沛齐", 19, c1),
    Student("Alex", 19, c1),
    Student("日天", 19, c2)
]

for obj in user_object_list:
    print(obj.name,obj.age, obj.class_object.title)

情景三:

class Student(object):
    """ 学生类 """

    def __init__(self, name, age, class_object):
        self.name = name
        self.age = age
        self.class_object = class_object

    def message(self):
        data = "我是一名{}班的学生,我叫:{},我今年{}岁".format(self.class_object.title, self.name, self.age)
        print(data)


class Classes(object):
    """ 班级类 """

    def __init__(self, title, school_object):
        self.title = title
        self.school_object = school_object


class School(object):
    """ 学校类 """

    def __init__(self, name):
        self.name = name


s1 = School("北京校区")
s2 = School("上海校区")

c1 = Classes("Python全栈", s1)
c2 = Classes("Linux云计算", s2)

user_object_list = [
    Student("武沛齐", 19, c1),
    Student("Alex", 19, c1),
    Student("日天", 19, c2)
]
for obj in user_object_list:
    print(obj.name, obj.class_object.title ,  obj.class_object.school_object.name)

面向对象之特殊方法

__str__

这个方法有2个注意点,

  1. str 必须返回字符串
  2. 触发这个方法是在把对象p用str(p)进行强转的时候才会触发,普通情况下 print(p)只能输出对象的内存地址.
    但是有时候像orm中希望看到这个对象的一些属性,就可以这么操作
class test():
    """类的描述"""
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def __str__(self):
        # 这个方法必须返回字符串
        return "对象self的属性name是%s,age是%s"%(self.name,self.age)

p = test("alex",18)
print(str(p))  # 对象self的属性name是alex,age是18

__call__

__call__的会在实例对象加括号运行的时候触发,比如p是实例对象,p({"age":18})在自我运行时候就出发了__call__方法
**{"age":18}会传给
kwargs**

class test():
    """类的描述"""
    def __init__(self,name):
        self.name=name
        
    def __call__(self, *args, **kwargs):
        print(self.name,args,kwargs)

p = test("alex")
p(**{"age":18})   # alex () {'age': 18}

__new__,__init__

这2个都是类初始化的时候自动运行的.注意有2点

  1. __new__比__init__先运行
  2. 如果__new__有返回值才会运行__init__,否则不会运行__init__ 下面代码示例__new__没有返回值,所以__init__没有运行,导致实例没有name属性
class test():
    """类的描述"""
    def __init__(self,name,age):
        print("init开始了")
        self.name=name
        self.age=age

    def __new__(cls, *args, **kwargs):
        print("new开始了")
        print(cls.__doc__)

p = test("alex",18)
print(p.name)  # AttributeError: 'NoneType' object has no attribute 'name'

__new__ 更多是用在单例模式下
单例模式也可以通过模块导入实现单例模式

class Student:
 
  _instance = None
 
  def __init__(self, name, age):
    self.name = name
    self.age = age
 
  def __new__(cls, *args, **kwargs):
    # if cls._instance:
    #   return cls._instance            # 有实例则直接返回
    # else:
    #   cls._instance = super().__new__(cls)    # 没有实例则new一个并保存
    #   return cls._instance            # 这个返回是给是给init,再实例化一次,也没有关系
 
    if not cls._instance:               # 这是简化的写法,上面注释的写法更容易提现判断思路
      cls._instance = super().__new__(cls)
    return cls._instance
 
 
stu1 = Student('jack', 18)
stu2 = Student('jack', 18)
print(stu1 is stu2)
print(stu1.__dict__, stu2.__dict__)
 
# 原理:和方法2类似,将判断的实现方式,从类的绑定方法中转移到类的__new__中
# 归根结底都是 判断类有没有实例,有则直接返回,无则实例化并保存到_instance中。


# ----------------------------------------------------------------
有时候多线程的情况下需要生成单例模式,那么需要加入锁的机制
from threading import Lock

class SingalClass(object):
    instance=None
    Loc = Lock()
    def __init__(self,name):
        self.name=name

    def __new__(cls, *args, **kwargs):
        # 考虑到可能多线程进行实例化,用锁的方式确保单例模式的创建
        with cls.Loc:
            if cls.instance:
                return cls.instance
            else:
                cls.instance=super().__new__(cls)
                return cls.instance

__repr__

repr是需要有return返回值的,并且在调用print的时候会触发

from threading import Lock

class SingalClass(object):
    instance=None
    Loc = Lock()
    def __init__(self,name):
        self.name=name

    def __new__(cls, *args, **kwargs):
        # 考虑到可能多线程进行实例化,用锁的方式确保单例模式的创建
        with cls.Loc:
            if cls.instance:
                return cls.instance
            else:
                cls.instance=super().__new__(cls)
                return cls.instance

    def __repr__(self):
        return "from repr,%s"%self.name

a = SingalClass("aaaa")
b = SingalClass("bbb")
c = SingalClass("ccc")
print(a,b ,c)
# from repr,ccc from repr,ccc from repr,ccc

这里三个实例都输出了name竟然是ccc,是因为他们使用了单例模式,self.name每次实例化都是同一个类的name属性会被不断赋值所覆盖

__dict__

查看类和对象中的所有成员

class test():
    """类的描述"""
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def __str__(self):
        # 这个方法必须返回字符串
        return "对象self的属性name是%s,age是%s"%(self.name,self.age)

p = test("alex",18)
print(p.__dict__)  # {'name': 'alex', 'age': 18}
print(test.__dict__) # {'__module__': '__main__', '__doc__': '类的描述', '__init__': <function test.__init__ at 0x00000229E10286A8>, '__str__': <function test.__str__ at 0x00000229F1464D90>, '__dict__': <attribute '__dict__' of 'test' objects>, '__weakref__': <attribute '__weakref__' of 'test' objects>}

__doc__

每个对象都会有一个__doc__属性,用于描述该对象的作用,相当于注解

class test():
"""类的描述"""
def init(self):
pass
def run(self):
"""方法的描述"""
pass
t = test()
print(t.doc) # 类的描述
print(t.run.doc) # 方法的描述

__getitem__,__setitem__,__delitem__

这三个方法就如同字典的键值增删改查一样.
如果实例对象按照对象的增删改查操作,就会对应触发这三个内置方法,详见代码示例

class test():
    """类的描述"""
    def __init__(self,name,age):

        self.name=name
        self.age=age

    def __getitem__(self, item):
        print("当前实例对象的属性有"%self.__dict__)
        print(self.__dict__.get(item))

    def __setitem__(self, key, value):
        print("当前实例对象的属性有%s,接下来添加的key是%s,value是%s"%(self.__dict__,key,value))
        # 当前实例对象的属性有{'name': 'alex', 'age': 18},接下来添加的key是home,value是北京
        self.__dict__[key]=value
        return self

    def __delitem__(self, key):
        print("要删除对象的key是%s"%key)  # 要删除对象的key是name
p = test("alex",18)
# __getitem__ 示例
p["name"]  # alex
p["name2"]  # None

# __setitem__ 示例
p["home"]="北京"
print(p.__dict__) # {'name': 'alex', 'age': 18, 'home': '北京'}

# __delitem__ 示例
del p["name"] #触发 __delitem__的方法

__getattr__,__setattr__,__delattr__

__getattr__和上面不同的是访问一个对象的属性时,如果对象没有该属性, 才会触发__getattr__方法
注意__setattr__, 对init里的属性设置也会触发__setattr__的方法

class test():
    """类的描述"""
    def __init__(self,name,age):

        self.name=name
        self.age=age


    def __getattr__(self, item):
        print("查询对象的%s属性了"%item)


    def __setattr__(self, key, value):
        print("设置一个键值对,key是%s,value是%s"%(key ,value))
        object.__setattr__(self,key,value)

    def __delattr__(self, item):
        print("删除对象的%s属性了" % item)

p = test("alex",18)
# __getattr__ 示例
p.name   # 查询对象的name属性了

# __setattr__ 示例
p.home="北京"

#设置一个键值对,key是name,value是alex
#设置一个键值对,key是age,value是18
#查询对象的name属性了
#设置一个键值对,key是home,value是北京

# __delattr__ 示例

del p.name  # 删除对象的name属性了

__getattr__,__getitem__,__getattribute__ 的使用区别

__getattr__, __getitem__ 这2者就是调用方式区别,前者是用object.attr 后者用object[attr]方式获取
他们的共同点就是当对象的属性不存在时候,调用会触发

__getattribute__ 只在新式类中可用,在访问任何属性(包括已经存在的和不存在的)时都会调用__getattribute__方法。
通常不推荐直接覆盖此方法,因为在实现时需要小心避免无限递归调用。

drf中request对象用 __getattribute__ __getattr__的封装原理
django中是有request对象的,在drf模块中对django的request对象再进行了封装,赋予了更多的属性
我们用代码模拟下

class django:
    """类的描述"""
    desc = "这个是django类"

    def __init__(self):
        pass

    def v1(self):
        print("django的v1方法")

    def v2(self):
        print("django的v2方法")


class drf:
    def __init__(self, req, xx):
        self._request = req
        self.xx = xx

    def v1(self):
        print("request的v1方法")


mytest = django()
request = drf(mytest, "xxxx")
request._request.v1()  # django的v1方法

这是一个类的组合用法,可以看到此时drf类中可以调用django类的v1方法了.但是drf中我们实际调用都是request.v1()这种语法调用的
因为默认django的方法本身就不存在drf类中,只是组合在一起了,这里我们使用__getattr__的特性,可以实现request.v1()调用到django类的方法

class django:
    """类的描述"""
    desc = "这个是django类"

    def __init__(self):
        pass

    def v1(self):
        print("test的v1方法")

    def v2(self):
        print("test的v2方法")


class drf:
    def __init__(self, req, xx):
        self._request = req
        self.xx = xx

    def __getattr__(self, attr):
        try:
            print("使用__getattr__方法添加属性>>%s" % attr)
            return getattr(self._request, attr)
        except AttributeError:
            print("异常使用__getattribute__方法添加属性>>%s" % attr)
            #这一条是为了调用父类的__getattribute__方法,但是因为drf和django都不存在的方法 调用必然报错,所以这里就是复用父类的抛出异常.
            return super().__getattribute__(attr)

    def v1(self):
        print("request的v1方法")


mytest = django()
request = drf(mytest, "xxxx")
# request._request.v1()  # test的v1方法
request.v2()  # 使用__getattr__方法添加属性>>v2    test的v2方法

print(request.desc)  # 使用__getattr__方法添加属性>>desc   这个是django类

__add__

add的方法会在实例对象进行相加的时候进行调用

class t():
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return self.name

    def __add__(self, other):
        print("other 参数>>", other)
        return "%s + %s" % (self.name, other.name)


a = t("naruto")
b = t("sasikei")
d = a + b
print(d)

#输出结果
other 参数>> sasikei
naruto + sasikei

__enter__,__exit__

这2个方法是对with上下文管理的,从with p as fwith语句开始就执行__enter__,运行结束执行__exit__
注意这里的f是__enter__的返回值,所以f.name才能输出p的name属性

class test():
    """类的描述"""
    def __init__(self,name,age):

        self.name=name
        self.age=age


    def __enter__(self):
        print("enter开始")
        return self


    def __exit__(self, exc_type, exc_val, exc_tb):
        print("eixt结束")
        pass

#p = test("alex",20)  这里可以不用这种写法,而采用下面的写法,因为`def __enter__(self)`中作了`return self`的处理.这里的self就是实例本身
# 也就是用test()类生成实例后再__enter__进行返回实例对象
with test("alex",20) as f:
    print(f.name)
    #enter开始
    #alex
    #eixt结束

上下文管理更多用的是对一些即开即关的服务的,比如开启一个示例查询数据库,查完以后及时关闭,避免一直后台和数据库保持连接,节约资源

class test():
    """类的描述"""
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def __enter__(self):
        self.conn="数据库的连接代码示例"
        return self.conn


    def __exit__(self, exc_type, exc_val, exc_tb):
        self.conn.close()
p = test("alex",20)
with p as f:
    print(f.curser())

顺便说下__exit__方法中的这个三个参数‘exc_type, exc_val, exc_tb’,
异常类型,异常值和异常的trackback
如果with语发生异常,但不希望抛出,__exit__方法应该返回return True,相当于with语句自动做一个try,except处理,

class test():

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        # <class 'NameError'>||name 'bbb' is not defined||<traceback object at 0x7ff0400fc7c0>
        print(exc_type,exc_val,exc_tb,sep="||")  
        return True
with test() as e:
    a=1
    bbb # 发生错误

面向对象归一化

归一化指的是我们在父类中创建模板,子类在创建的时候必须要遵循父类的规则.比如下面的,我们要遵从父类Animal的规则,必须在子类创建run和eat的方法.否则无法正常实例化.
这个归一化的模块用的是abc,设置如下,而Animal这个类本身作用也只是为了限制规范,并无自己的方法,我们称为抽象类.
这种把abc.ABCMeta作为父类抽象类,自身是无法被实例化的.

import abc

class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def run(self):
        pass
    @abc.abstractmethod
    def eat(self):
        pass

    def sleep(self):
        pass


class People(Animal):
    def __init__(self):
        pass

    def wear(self):
        pass
    

a=People()

报错信息如下:
Traceback (most recent call last):
  File "D:/PyCharm 2018.2.4/test/test.py", line 1582, in <module>
    a=People()
TypeError: Can't instantiate abstract class People with abstract methods eat, run

类的属性方法隐藏

一般不想让外部调用类的方法或者属性,可以前面加__进行隐藏.但是隐藏只是改变了调用规则,并不是真正意义的禁止调用
有三大特性如下
特性1: __开头的属性或者方法 直接调用会报错,因为__的机制会把属性重新修改为_Classname__attribute的格式,具体可以查看__dict__
特性2 所以虽然是隐藏,但是知道里面的修改机制,还是可以直接调用
特性3 __的方法和属性无法覆盖父类的.比如c的__type实际已经被内部转成了_China__type,而父类的是_People__type.所以他们不重名,也无法被覆盖

class People():
    __type="人类"
    home="地球"
    def __init__(self,name):
        self.name=name

    def __hobby(self):
        print("%s喜欢自由"%self.name)

    def attr(self):
        self.__hobby()

    def run(self):
        print("%s开始跑步"%self.name)

class China(People):
    __type = "中国人"
    def __init__(self,name,home):
        super().__init__(name)
        self.home=home


a=People("alex")
#特性1: __开头的属性或者方法 直接调用会报错,因为__的机制会把属性重新修改为_Classname__attribute的格式,具体可以查看__dict__
#a.__hobby()       # AttributeError: 'People' object has no attribute '__hobby'
#print(a.__type)  #  AttributeError: 'People' object has no attribute '__type'

# 特性2 所以虽然是隐藏,但是知道里面的修改机制,还是可以直接调用
print(a._People__type)    # 人类
# 特性3 类的内部调用可以直接调
a.attr()    # alex喜欢自由

c = China("周杰伦","台湾")
# 特性3 __的方法和属性无法覆盖父类的.比如c的__type实际已经被内部转成了_China__type,而父类的是_People__type.所以他们不重名,也无法被覆盖

类的反射

一切皆对象

在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("武沛齐","wupeiqi666")
user_object.name
  • 类是对象
class Person(object):
    title = "武沛齐"

Person.title
# Person类也是一个对象(平时不这么称呼)
  • 模块是对象
import re
re.match
# re模块也是一个对象(平时不这么称呼)。

反射的内置函数

getattr,setattr,hasattr,delattr
由于反射支持以字符串的形式去对象中操作成员【等价于 对象.成员 】,所以,基于反射也可以对类、模块中的成员进行操作。

import_module + 反射

平时我们要导入模块 使用的是import random 或者from datetime import datetime
我们还可以使用import_module进行反射导入,

示例一
from importlib import import_module
m = import_module("random")
v1 = m.randint(1,100)
示例二
# 导入模块exceptions,获取exceptions中的InvalidURL类。
from requests.exceptions import InvalidURL
# 使用反射导入
from importlib import import_module
m = import_module("requests.exceptions") 
# 去模块中获取类
cls = m.InvalidURL
示例三
params = importlib.import_module('b.c.c') #绝对导入
params_ = importlib.import_module('.c.c',package='b') #相对导入

注意: 使用反射导入的时候,只能写到模块的文件这一层,也就是模块所在的py文件这一层
比如 requests.exceptions exceptions一定是exceptions.py 而InvalidURL只是该文件里面的一个类

使用场景

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

再比如
我们再run函数中要运行邮件,微信和短信发送提醒功能.通过反射可以实现,后续如果要关闭或者开启功能,
也只需要在config文件中把MESSAGE_HANDLER_LIST的功能列表进行注释或者取消注释就能达到目的,
无需修改run函数里的代码.实现功能的动态扩展

类的继承顺序算法

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

示例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


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

示例三

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]

示例四


简写为: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]

其他计算过程省略

c3算法总结

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

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中丢弃经典类,只保留新式类。

will change into a diamond diagram under the new system:

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

经典类的继承关系在钻石模型中会有bug, 按照经典类的继承顺序,不留顶端,那么查找顺序会是D-B-OBJECT-C   那么此时本该继承object基类的C因为继承顺序无法继承.
详细文档:
	https://www.python.org/dev/peps/pep-0253/#mro-method-resolution-order-the-lookup-rule
	https://www.python.org/download/releases/2.3/mro/

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

  • Py2:

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

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

      class Foo(object):
          pass
      
      或
      
      ```python
      

      class Base(object):
      pass

      class Foo(Base):
      pass
      ```

  • Py3

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

      class Foo:
          pass
      
      class Bar(object):
          pass
      

内置函数补充

本次要给讲解的内置函数共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'] = "武沛齐"
    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'] = "武沛齐" # __setitem__
    info["age"] = 18       # __setitem__
    print(info)
    
    value = info.get("age")
    
    print(value)
    
  • type,获取一个对象的类型。

    v1 = "武沛齐"
    result = type(v1)
    print(result) # <class 'str'>
    
    v2 = "武沛齐"
    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
    

异常处理

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)

异常处理的基本格式

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关闭文件。
"""    

内置的异常

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
"""

自定义异常和场景

上面都是Python内置的异常,只有遇到特定的错误之后才会抛出相应的异常。
我们自定义的异常,如果想要触发,则需要使用:raise MyException()类实现。


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)

使用场景

案例一:你我合作协同开发,你调用我写的方法。

  • 我定义了一个函数

    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()
    	# 发送邮件代码...
        # ...
    
  • 你调用我写的函数

    def execute():
        # 其他代码
        # ...
        
    	try:
            send_email(...)
        except EmailValidError as e:
            pass
        except ContentRequiredError as e:
            pass
        except Exception as e:
            print("发送失败")
    
    execute()
    
    # 提示:如果想要写的简单一点,其实只写一个Exception捕获错误就可以了。
    

案例二:在框架内部已经定义好,遇到什么样的错误都会触发不同的异常。

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

案例三:比如django的form组件的调用和钩子抛出的异常都具备特殊的含义

特殊的finally

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

print("end")

当在函数或方法中定义异常处理的代码时,要特别注意finally和return。

def func():
    try:
        return 123
    except Exception as e:
        pass
    finally:
        print(666)
        
func()

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

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
>>> 成功

源码的继承练习

class BaseServer:

    def __init__(self, server_address, request_handler_class):
        self.server_address = server_address
        self.request_handler_class = request_handler_class

    def serve_forever(self):
        while True:
            request, client_address = self.get_request()
            self.process_request(request, client_address)

    def finish_request(self, request, client_address):
        self.request_handler_class(request, client_address, self)()

    def process_request(self, request, client_address):
        pass

    def get_request(self):
        return "傻儿子", "Alex"


class  TCPServer(BaseServer):
    address_family = socket.AF_INET

    socket_type = socket.SOCK_STREAM

    request_queue_size = 5

    allow_reuse_address = False

    def __init__(self, server_address, request_handler_class, bind_and_activate=True):
        BaseServer.__init__(self, server_address, request_handler_class)
        self.socket = socket.socket(self.address_family, self.socket_type)
        self.server_bind()
        self.server_activate()

    def server_bind(self):
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.socket.bind(self.server_address)
        self.server_address = self.socket.getsockname()

    def server_activate(self):
        self.socket.listen(self.request_queue_size)

    def get_request(self):
        return self.socket.accept()

    def close_request(self, request):
        request.close()


class ThreadingMixIn:
    def process_request_thread(self, request, client_address):
        self.finish_request(request, client_address)
        self.close_request(request)

    def process_request(self, request, client_address):
        t = threading.Thread(target=self.process_request_thread, args=(request, client_address))
        t.start()


class ThreadingTCPServer(ThreadingMixIn, TCPServer):
    pass


class BaseRequestHandler:
    def __init__(self, request, client_address, server):
        self.request = request
        self.client_address = client_address
        self.server = server
        self.setup()

    def __call__(self, *args, **kwargs):
        try:
            self.handle()
        finally:
            self.finish()

    def setup(self):
        pass

    def handle(self):
        pass

    def finish(self):
        pass


class MyHandler(BaseRequestHandler):
    def handle(self):
        print(self.request)
        self.request.sendall(b'hahahahah...')


server = ThreadingTCPServer(("127.0.0.1", 8000), MyHandler)
server.serve_forever()

posted @ 2022-05-23 21:59  零哭谷  阅读(43)  评论(0编辑  收藏  举报