Python----类的方法(三)

with 上下文协议:

主要调用类的__enter__ , __exit__ 函数。当调用open类时,触发__enter__函数,并将返回值赋值给f(句柄),然后执行with下面的代码块,如果代码块没有错误,则在执行完代码块后自动触发__exit__函数。如果有错误,并不会直接报错,而是先进入__exit__函数退出文件操作后,再报错。如果在__exit__函数中设置返回值为真则会将异常吞下,否则会报错。

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

    # 进入函数,当上下文协议开始时调用,返回值给f(句柄)
    def __enter__(self):
        print('entering')
        return self

    # 退出函数,在退出的时候调用
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('exited')
        print(exc_type,exc_val,exc_tb)  #  exc_type 报错异常的类,exc_val 异常的信息,exc_tb  追踪信息
        return True  # 不会报错,吞下了错误

with Open('a.txt') as f:  # 触发__enter__
    print(afsdfdsdv)
    #  print(fehgadfgv)  会直接退出先不报错,触发__exit__函数



>>> entering
>>> exited
>>> <class 'NameError'> name 'afsdfdsdv' is not defined <traceback object at 0x000001930DC42288>

描述符的小应用:

Python是一个弱类型的编程语言,函数之间传递参数时,并不用明确指出参数类型。下面对函数的参数类型做判断

class Type:
    def __init__(self, key):
        # 方便在底层字典添加参数
        self.key = key
        # 定义一个类型字典,用于判断
        self.dict = {'name':str, 'age':int}
    
    # instance 是关联类
    def __set__(self, instance, value):
        print('set method')
        # 判断传入的类型是否是对应的类型
        if type(value) is not self.dict[self.key]:
            print(self.key + 'error')
        instance.__dict__[self.key] = value

    def __get__(self, instance, owner):
        print('get method')
        return instance.__dict__[self.key]

    def __delete__(self, instance):
        print('delete method')
        return instance.__dict__.pop(self.key)


class Test:
    # 实例数据描述符
    name = Type('name')
    age = Type('age')
    
    def __init__(self,name,age):
        self.name = name
        self.age = age

t = Test('Jax', '15')

装饰器:

※本质就是一个函数,为其他函数添加附加功能。不仅能将函数当参数传递,类也可以当成参数进行传递。
原则:1、不修改被修饰函数的代码  2、不修改被修饰函数调用方式  

装饰器也可以是类:@符号在做 area=prop(area),所以既可以是函数也可以是类 

简单的小例子:闭包

import time
def wrapper(func):   
    def deco():
        t1 = time.time()
        func()
        t2 = time.time()
        print(t2 - t1)
    return deco
@wrapper  # 先传递函数名
def test():
    for i in range(10):
        print(i)
test()


>>> 0
1
2
3
4
5
6
7
8
9
0.0009980201721191406

python里一切皆对象,函数也有类的双下划线方法,因为他们都是对象

def wrapper(func):
    pass
    return func


@wrapper  # 不会关下面的东西,都会当成类来处理   test = wrapper(test)
def test():
    pass


test.x = 1
print(test.__dict__)


>>> {'x': 1}

更高级的玩法:为类添加功能的装饰器。

def deco(**kwargs):
    def wrapper(func):
        for i,j in kwargs.items():
            setattr(func,i,j)
        return func
    return wrapper


@deco(x=1,y=2)  # 1.先deco(x=1,y=2)返回---->wrapper 2.相当于执行@wrapper再将类传入wrapper中
class Foo:
    pass
print(Foo.__dict__)

@deco(name = 'Jax')
class Pig:
    pass
print(Pig.__dict__)


>>> {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None, 'x': 1, 'y': 2}
{'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Pig' objects>, '__weakref__': <attribute '__weakref__' of 'Pig' objects>, '__doc__': None, 'name': 'Jax'}

装饰器+描述符:

对上面限制的类型限制的更简洁的写法:

通过装饰器,减少代码的冗余

def deco(**kwargs):
    def wrapper(obj):
        for key, val in kwargs.items():
            # 将属性字典的值对应描述符,覆盖原属性值
            setattr(obj, key, Typed(key, val))
        return obj  # 闭包
    return wrapper


class Typed:
    def __init__(self,key,val):
        self.key = key
        self.val = val

    def __set__(self, instance, value):
        if type(value) is not self.val:
            print('error')


@deco(name=str, age=int)
class People:
    def __init__(self, name, age):
        self.name = name
        self.age = age

People('Jax',19.9)

还可以实现静态属性的方法:

class Lazy:
    def __init__(self,area):
        self.area = area

    def __get__(self, instance, owner):
        # 当类去调用时触发,返回自身
        if instance is None:
            return self
        return self.area(instance)


class Room:
    def __init__(self):
        pass

    # @lazy(x=1)  1.运行lazy(x=1) 拿到返回的结果在  2.@返回值 ----> area = Lazy(area)
    @Lazy  #  1.area=Lazy(area), 实例化了一个对象,@就是一个语法糖.类自己多了一个属性
    
    def area(self):
        print('area method)

r = Room()
r.area  # 触发__get__方法
print(Room.area)  # 不再是执行area函数而是在执行area属性,已经被代理了

※ 从上到下遇见装饰器时,先运行装饰器。因为这是一个实例化的过程,静态属性主要给实例去用。

实现类方法:

class Statics:
    def __init__(self,parms):   # 装饰器触发此函数
        self.parms = parms

    def __get__(self, instance, owner):
        return self.parms(instance)
class People:
    name = 'Jax'

    @Statics
    def re(self):
        return '66666'

print(People.re)
p = People()
print(p.re)
print(People.__dict__)


>>> 66666
66666
{'__module__': '__main__', 'name': 'Jax', 're': <__main__.Statics object at 0x000001D64C8E30F0>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}

静态属性也可以实现类型检测:

静态属性被定义好以后无法被修改,如果再定义一个同名的方法,前面加上 方法名.setter 装饰器 就会在修改属性时触发该方法。
如果再定义一个同名的方法,前面加上 方法名.deleter 装饰器 就会在删除属性时触发该方法。

 

class Test:
    def __init__(self,count, one):
        self.count = count
        self.one = one

    @property    # 当有取值操作时触发
    def price(self):
        price = self.count * self.one
        return price

    @price.setter  # 当有赋值操作时触发
    def price(self,val):
        self.count = val

    @price.deleter  # 当有删除操作时触发
    def price(self):
        del self.count

t = Test(15,20)
print(t.price)
t.count = 30  # 触发setter函数
print(t.price)

另外一种定义静态属性的方法:

class Test:
    def get_oppo(self):
        print('oppo')
    def set_oppo(self,value):
        print('nova',value)
    def del_oppo(self):
        print('deletere')                       

                                                 #  上面函数定义的顺序可以变
    aaa = property(get_oppo,set_oppo,del_oppo)   #  顺序不能变  函数定义顺序不能变
t = Test()

实现类型检测:

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

    @property
    def name(self):
        print('get')

    @name.setter
    def name(self,val):
        if type(val) is not str:
            print('name error')

    @property
    def age(self):
        print('get')

    @age.setter
    def age(self, val):
        if type(val) is not int:
            print('age error')

t = Test(19,19.9)


>>> name error  age error

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值