风清扬

导航

Python之路(八)——Python OOP(进阶篇)

本节内容:

  1. 一切皆对象
  2. 元类
  3. 类的内置属性
  4. 描述符
  5. 类的装饰器
  6. 再看@property
  7. 反射

一、一切皆对象

统一两点

  • 对象与类为实现关系,类与类为继承关系
  • 谈对象和类是在特定场合下才有效,即 语言的场景性

使用内置属性查看对象的类

type vs instance vs issubclass

class Fu:
    pass
class Zi(Fu):
    pass
class Sun(Zi):
    pass

z = Zi()
print(isinstance(z,Fu))#判断一个对象是否为一个类的实例,可传递
print(isinstance(z,Zi))
print(type(z) is Fu) #False.仅判断实例和其产生的直接类的关系
print(type(z) is Zi)
print(issubclass(Zi,Fu)) #判断两个类的关系,可传递
print(issubclass(Sun,Fu))

python中类与对象关系图

说明:

  1. 实现关系:横向为实现关系,能够生成类的结构叫“元类”,生成的类本身也是对象所以也叫类对象
  2. 继承关系:纵向为继承关系,内置的类都继承自object
  3. 用户一般使用第二、三列,自定义类(MyClass 默认继承自object);常用变量(var2)是第二列内置类(str)的实现

二、元类

类的动态创建

  • 类代码定义在函数中,动态调用函数
  • 使用内部函数 type(name, bases, dict)动态创建
#方法一、函数内部定义类,动态调用函数创建
def choose_class(class_name):
    if class_name == 'foo':
        class Foo:
            pass
        return Foo
    if class_name == 'bar':
        class Bar:
            pass
        return Bar

foo = choose_class('foo')
print(foo) # <class '__main__.choose_class.<locals>.Foo'>
#分析:类的代码还需要提前书写

#方法二、type(name, bases, dict) -> a new type
# class MyClass:
#     bar = True
#两者等价
MyClasss = type('MyClass',(object,),{'bar':True})
print(MyClasss,MyClasss.bar) # <class '__main__.MyClass'> True

元类

创建类这种对象的类,type为Python中的内建元类

自定义元类

class UpperAttrMetaclass(type): #建议继承内部元类创建自己元类
    def __new__(cls, name, bases, dct):

        attrs = ((name, value) for name, value in dct.items() if not name.startswith('__'))
        uppercase_attr = dict((name.upper(), value) for name, value in attrs)
        return super(UpperAttrMetaclass, cls).__new__(cls, name, bases, uppercase_attr)

class Foo(object,metaclass=UpperAttrMetaclass):
    # __metaclass__ = UpperAttrMetaclass 3.7测试无效的
    bar = 'bip'

print(Foo.__dict__) # {'BAR': 'bip'...} 类的属性名被转换为大写  

三、内置属性

由于一切皆对象,python中的内置函数和操作符号(例如:.、 [] 、()、with 等)都对应类(对象)中的内置方法

内置方法很杂,按OOP思想要调用应该把内置方法封装为接口,由用户来实现(个人理解)

内置数据属性

  • __doc__ :类文档,子类不继承
  • __module__:类的模块名(含包名)
  • __dict__:类属性,含数据和方法属性,对象不显示类中的属性
  • __bases__:父类,多个用元祖组成
  • __class__:当前类名

内置方法属性

  • __new__ 创建对象,静态方法,类() 触发
  • __init__  初始化对象,由__new__返回本类对象时触发
# __new__ vs __init__
#1.
# class A(object):
#     def __init__(self):
#         print("init")
#     #默认为类方法,cls 内容由解释器自动传入,args 为父类元祖,kwargs 为类属性字典
#     def __new__(cls,*args, **kwargs):
#         print("new %s"%cls)
#         return object.__new__(cls, *args, **kwargs) #返回一个对象可以是自己的也可以是其他的类,
#
# A()
# 结果:
# new <class '__main__.A'>
# init

#2.
class A():
    pass
class B(A):
    def __init__(self):
        print("init")
    #默认为类方法,cls 内容由解释器自动传入,args 为父类元祖,kwargs 为类属性字典
    def __new__(cls,*args, **kwargs):
        print("new %s"%cls)
        return object.__new__(A, *args, **kwargs) #返回一个对象可以是自己的也可以是其他的类,

B() # new <class '__main__.B'>
  • __del__: del 对象时触发
class A():
    def __del__(self):
        print('__del__被执行了')

a = A()
del a # __del__被执行了。对象的内存释放由解释器来完成,程序员不用考虑
  • __str__ :显示对象打印格式
  • __repr__:同上,区别:命令行下,执行repr(对象名)打印,一般用作程序调试
class Employee:
    def __init__(self,name,age,salary):
        self.name = name
        self.age = age
        self._salary = salary

    def __str__(self):
        return f'name: {self.name}, age:{self.age}'

    def __repr__(self):
        return self.__str__() #懒惰写法
    
e = Employee('alex', 20, 5000)
# print(e) # 默认打印 <__main__.Employee object at 0x00000193C7C73EB8>
print(e) # __str__ name: alex, age:20 。 __repr__不会被print 等用户使用的函数调用
  • __getattr__ :对象取属性(不包含方法属性调用)触发
  • __setattr__:对象属性被修改时(包含方法属性调用)触发
  • __delattr__:删除对象属性时触发
  • __getattribute__:"对象名."  形式就会触发,不区分赋值还是取值,删除等操作。
class Foo:
    x = 1
    def __init__(self, y):
        self.y = y #会触发 setattr
        
    #主要对象. 这种形式都会触发
    def __getattribute__(self, item):
        print('----> from getattribute')

    def __getattr__(self, item):
        print('----> from getattr:你找的属性不存在')
    def __setattr__(self, key, value):
        print('----> from setattr')
        # self.key = value  #错误,引发死循环
        # self.__dict__[key] = value #这种写法
    def __delattr__(self, item):
        print('----> from delattr')
        # del self.item #无限递归了
        # self.__dict__.pop(item)

# __setattr__添加/修改属性会触发它的执行
f1 = Foo(10)
print(f1.__dict__)  # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值
f1.z = 3
print(f1.__dict__)

# __delattr__删除属性的时候会触发
# f1.__dict__['a'] = 3  # 直接修改属性字典,不触发
del f1.a
print(f1.__dict__)
#
# __getattr__只有在使用点调用属性且属性不存在的时候才会触发
f1.xxxxxx
  • __getitem__:同__getattr__,不过不是采用"."来操作而是"[ ]"
  • __setitem__
  • __delitem__
class Foo:
    def __init__(self,name):
        self.name=name

    def __getitem__(self, item):
        print(self.__dict__[item])

    def __setitem__(self, key, value):
        self.__dict__[key]=value
    def __delitem__(self, key):
        print('del obj[key]时,我执行')
        self.__dict__.pop(key)
    def __delattr__(self, item):
        print('del obj.key时,我执行')
        self.__dict__.pop(item)

f1=Foo('sb')
f1['age']=18
f1['age1']=19
del f1.age1
del f1['age']
f1['name']='alex'
print(f1.__dict__)
  • __call__:对象名() 时被触发
class Foo:

    def __init__(self):
        pass

    def __call__(self, *args, **kwargs):
        print('__call__')


obj = Foo()  # 执行 __init__
obj()  # 执行 __call__
class Meta(type):
    #2. 生成类对象
    def __new__(cls, name, bases, dct):
        print("calling Meta's __new__", cls)
        return type.__new__(cls, name, bases, dct)
    #4.类对象调用触发元类的__call__ 方法
    def __call__(cls, *args, **kwargs):
        print("calling Meta's __call__", cls)
        #5.类对象,从自己的对象觉都看也是一个类,可以直接调用自己的静态方法 __new__ 生成自己对象
        i = cls.__new__(cls)
        #6.类对象生成的对象初始化
        i.__init__(*args, **kwargs)
        return i

class A(metaclass=Meta): # 1. 类对象的创建使用 元类的__new__ 
    def __new__(cls, *args, **kwargs):
        print("calling A's __new__")
        return object.__new__(cls)

    def __init__(self, *args, **kwargs):
        print("calling A's __init__")
#3.类对象调用        
A()
# calling Meta's __new__ <class '__main__.Meta'>
# calling Meta's __call__ <class '__main__.A'>
# calling A's __new__
# calling A's __init__
  • __enter__ :进入with 代码块被执行
  • __exit__ : 退出with 代码块前被执行 
class Open:
    def __init__(self,filepath,mode='r',encoding='utf-8'):
        self.filepath=filepath
        self.mode=mode
        self.encoding=encoding

    def __enter__(self):
        # print('enter')
        self.f=open(self.filepath,mode=self.mode,encoding=self.encoding)
        return self.f

    def __exit__(self, exc_type, exc_val, exc_tb):
        # print('exit')
        self.f.close()
        return True  #不处理异常
    def __getattr__(self, item):
        return getattr(self.f,item)

with Open('a.txt','w') as f:
    print(f)
    f.write('aaaaaa')
    f.wasdf #抛出异常,交给__exit__处理
  • __next__ : 定义可迭代对象

  • __iter__: 定义迭代器
class Fib:
    def __init__(self):
        self._a=0
        self._b=1

    def __iter__(self):
        return self

    def __next__(self):
        self._a,self._b=self._b,self._a + self._b
        return self._a

f1=Fib()
print(f1.__next__())
print(next(f1))
print(next(f1))
#继续迭代
for i in f1:
    if i > 100:
        break
    print('%s ' %i,end='')

四、描述符

描述符本质就是一个新式类(Python3都是新式类),在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个。其作用是用来代理另外一个类的类属性

#描述符Str
class Str:
    def __get__(self, instance, owner):
        print('Str调用')
    def __set__(self, instance, value):
        print('Str设置...')
    def __delete__(self, instance):
        print('Str删除...')

#描述符Int
class Int:
    def __get__(self, instance, owner):
        print('Int调用')
    def __set__(self, instance, value):
        print('Int设置...')
    def __delete__(self, instance):
        print('Int删除...')

class People:
    name=Str()
    age=Int()
    def __init__(self,name,age): #name被Str类代理,age被Int类代理,
        self.name=name
        self.age=age


p1=People('alex',18)
#Str设置...
# Int设置...
print(p1.__dict__) # {}
print(People.name) # Str调用,自动触发描述符__get__ 方法
print(People.__dict__)
# 'name': <__main__.Str object at 0x000001CC5EAD2208>, 'age': <__main__.Int object at 0x000001CC5EAD2240>

分类

  • 描述符中仅定义__get__,称 非数据描述符

  • 定义了__set__或者__del__之一 ,称 数据描述符  

优先级(从高到低)

  1. 类属性
  2. 数据描述符
  3. 实例属性
  4. 非数据描述符
  5. __getattr__(没有属时调用)
#数据描述符Str
class Str:
    def __get__(self, instance, owner):
        print('Str调用')
    def __set__(self, instance, value):
        print('Str设置...')
    def __delete__(self, instance):
        print('Str删除...')
#非数据描述符
class Int:
    def __get__(self, instance, owner):
        print('Int调用')

class People:
    name=Str()
    age = Int()
    def __init__(self,name,age): #name被Str类代理
        self.name = name
        self.age = age
    def __getattr__(self, item):
        print("没有找到")
#1.类属性调用
# People.name = 'alex'
# People.age = 18
#相当于覆写了字典
# print(People.name, People.age)
# print(People.__dict__)

#2.对象属性
p1 = People('alex2',118)
print(p1.name, p1.age)
print(p1.__dict__)
# Str设置...
# Str调用
# None 118
# {'age': 118}
#非数据描述符代理 没有启用; 数据描述符代理 启用

#3.没有定义的属性
# p1.PPP #触发__getattr__方法

作用<例:实现对象初始化参数类型限制>

#参数类型限制

class Typed:
    def __init__(self,name,expected_type):
        self.name=name
        self.expected_type=expected_type
    def __get__(self, instance, owner):
        print('get--->',instance,owner)
        if instance is None:
            return self
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print('set--->',instance,value)
        if not isinstance(value,self.expected_type):
            raise TypeError('Expected %s' %str(self.expected_type))
        instance.__dict__[self.name]=value
    def __delete__(self, instance):
        print('delete--->',instance)
        instance.__dict__.pop(self.name)


class People:
    name=Typed('name',str)
    age=Typed('name',int)
    salary=Typed('name',float)
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary

p1=People('123',18,3333.3)
# set---> <__main__.People object at 0x0000019D389623C8> 123
# set---> <__main__.People object at 0x0000019D389623C8> 18
# set---> <__main__.People object at 0x0000019D389623C8> 3333.3
# p1=People('egon','18',3333.3)
# p1=People('egon',18,3333)  

五、类的装饰器

装饰器形式:

  1. 被装饰对象:类、普通函数、类方法
  2. 装饰者:普通函数;描述符方法(用描述符自身代替 @描述符名)

回顾装饰器(@xxx)

#1.简单装饰器
# def wrapper(fun,*args,**kwargs):
#     print("简单装饰器")
#     return fun
#
# @wrapper #@装饰器名相当于: test1=wraperr(test1(*args,**kwargs))
# def test1(a,b):
#     print("test1",a,b)

# test1(2,3)
#2.带参数的装饰器
def log(log_level):
    def wrapper(fun,*args,**kwargs):
        if log_level == 'Error':
            print("Error 级别的日志")
        return fun
    return wrapper

@log('info')    #带参数的装饰器相当于:1.执行带参函数返回装饰器 2. test1=wraperr(test1(*args,**kwargs))
def test1(a,b):
    print("test1",a,b)  

类的装饰器<重构类型限制>

class Typed:
    def __init__(self,name,expected_type):
        self.name=name
        self.expected_type=expected_type
    def __get__(self, instance, owner):
        print('get--->',instance,owner)
        if instance is None:
            return self
        return instance.__dict__[self.name]
    def __set__(self, instance, value):
        print('set--->',instance,value)
        if not isinstance(value,self.expected_type):
            raise TypeError('Expected %s' %str(self.expected_type))
        instance.__dict__[self.name]=value
    def __delete__(self, instance):
        print('delete--->',instance)
        instance.__dict__.pop(self.name)

def typeassert(**kwargs):
    def decorate(cls):
        print('类的装饰器开始运行啦------>',kwargs)
        for name,expected_type in kwargs.items():
            setattr(cls,name,Typed(name,expected_type))
        return cls
    return decorate

@typeassert(name=str,age=int,salary=float)
#有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People)
class People:
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary

print(People.__dict__)
p1=People('egon',18,3333.3)

类方法加装饰器<制作property>

#模拟@property
class Lazyproperty:
    def __init__(self,func):
        self.func=func
    def __get__(self, instance, owner):
        print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')
        if instance is None:
            return self
        return self.func(instance) #此时你应该明白,到底是谁在为你做自动传递self的事情

class Room:
    def __init__(self,name,width,length):
        self.name=name
        self.width=width
        self.length=length

    @Lazyproperty #area=Lazyproperty(area) 相当于定义了一个类属性,即描述符
    def area(self):
        return self.width * self.length

r1=Room('alex',1,1)
print(r1.area)  

描述符与装饰器

  • 描述符:对类的属性(数据,方法)进行描述的类。在被代理类的属性访问时触发__get__,__set__,__del__
  • 装饰器本质也是实现一种代理功能。采用@语法糖,触发条件取决于代理者

六、再看@property

Python内置的描述符property,采用装饰器@的方式可以对类数据属性的访问进行控制

用法一

# 用法一、
class Foo:
    @property
    def AAA(self):
        print('get的时候运行我啊')

    @AAA.setter
    def AAA(self,value):
        print('set的时候运行我啊')

    @AAA.deleter
    def AAA(self):
        print('delete的时候运行我啊')

#只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter
f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA

用法二

#用法二
class Foo:
    def get_AAA(self):
        print('get的时候运行我啊')

    def set_AAA(self,value):
        print('set的时候运行我啊')

    def delete_AAA(self):
        print('delete的时候运行我啊')
    AAA=property(get_AAA,set_AAA,delete_AAA) #内置property三个参数与get,set,delete一一对应

七、反射

反射是指一类应用,它们能够自描述和自控制。也就是说,这类应用通过采用某种机制来实现对自己行为的描述(self-representation)和监测(examination),并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。——from 百度

  • __hasattr__
  • __getattr__
  • __setattr__
  • __delattr__
#动态生成函数
class GreetMe:
    def __init__(self, name):
        self.name = name

    def __getattr__(self, attr):
        allowed = ['hello', 'goodbye', 'goodnight']

        def call(name=None):
            if attr in allowed:
                target = name if name else self.name
                print(f"{target} {attr.capitalize()}")
            else:
                raise AttributeError(f"{attr} is not in {__class__}")

        return call


greet = GreetMe('Luna')
greet.hello('Peter')
greet.goodbye()
greet.he2llo('Peter')

 

 

posted on 2019-01-27 17:12  卜戈的博客  阅读(752)  评论(0编辑  收藏  举报