描述符及类的装饰器应用实现类型检查,自定制property,property补充,property实现类型检查

'''描述符及类的装饰器应用'''
# class Typed:
# '''这是一个数据描述符'''
# def __init__(self, one, two):
# self.one = one
# self.two = two
#
# def __set__(self, instance, value): # 此步操作是People类实例化时或者单独给类设置值时才会触发
# if type(value) == self.two:
# instance.__dict__[self.one] = value
# else:
# raise TypeError('输入类型错误!')
#
# def __get__(self, instance, owner): # ③然后执行的是__get__方法
# # return instance.__dict__[self.one]
# print('***')
#
# def __delete__(self, instance):
# instance.__dict__.pop(self.one)
#
#
# def deco(**kwargs):
# '''这是一个装饰器'''
# def wrapper(func):
# for key,val in kwargs.items():
# setattr(func, key, Typed(key, val)) # 设置类属性
# return func
# return wrapper
#
#
# @deco(name=str, age=int, salary=float, gender=str, height=int)
# class People:
# '''这是一个类,实现了*定制类属性及实例化参数类型限制'''
# # name = Typed('name', str) # ②所以访问的是这一步
# # age = Typed('age', int)
# # salary = Typed('salary', float)
# # gender = Typed('gender', str)
# # height = Typed('height', int)
# def __init__(self, name, age, salary, gender, height):
# self.name = name
# self.age = age
# self.salary = salary
# self.gender = gender
# self.height = height

# p1 = People('alex', 18, 1000.55, 'x', 170)
# p1.name # ①数据描述符大于实例属性
# print(People.__dict__)


'''自定制property'''
# class Lazyproperty:
# def __init__(self, func):
# # print(func) # <function Room.area at 0x000001C226872EE0>
# self.func = func
#
# def __get__(self, instance, owner):
# if instance is None:
# return self # 当类调用时,返回实例本身(即一个内存地址)
# return self.func(instance)
#
# class Room:
# def __init__(self, name, width, length):
# self.name = name
# self.width = width
# self.length = length
#
# @Lazyproperty # area = Lazyproperty(area),由类来作为装饰器,此步操作相当于实例化过程;相当于是给类设置类属性,所以可以让Lazyproperty写成描述符
# # @property # area = property(area)
# def area(self): # 由property装饰器装饰时:'area': <property object at 0x000001940DF045E0>
# return self.width * self.length
#
# def func(self): # 'func': <function Room.func at 0x000001940DF02E50>
# return '...'

# r1 = Room('客厅', 6, 7)
# print(r1.area.func(r1)) # 没有__get__方法时,r1为Room实例,area由装饰器修饰后为Lazyproperty实例,所以有func,加上括号运行,参数为实例本身即r1
# print(Room.__dict__)
# print(r1.area)

'''自我总结:这个装饰器不再是函数,而是一个类,且这个类属于描述符;在@的时候这一步相当于实例化,也等同于描述符设置类属性的过程;此时描述符有个__get__方法,
instance是Room类的实例r1,owner是Room类,而self.func指向的是Room类的area属性地址,加括号即可运行;所以__get__方法等同于运行了Room类
的area方法,而r1.area因为描述符的原因会去找Lazyproperty中的__get__方法'''

# 当实例调用时,__get__方法的instance为实例本身;当类调用时,instance为None,所以自己这样的定制的装饰器会报错,而python提供的property
# 则返回一个对象内存地址,那么加上一个if判断即可;此时类调用也就不会报错了
# print(Room.area)


'''自定制property实现延迟计算功能'''
# class Lazyproperty1:
# def __init__(self, func):
# self.func = func
#
# def __get__(self, instance, owner):
# # print(self) # <__main__.Lazyproperty1 object at 0x000001C6EE413D30>
# print('get')
# if instance is None:
# return self
# setattr(instance, self.func.__name__, self.func(instance)) # 设置实例字典属性,因为是非数据描述符,所以优先级的问题,实例属性有的,就不会去非数据描述符去找
# return self.func(instance)
#
# class Room1:
# def __init__(self, name, width, length):
# self.name = name
# self.width = width
# self.length = length
#
# @Lazyproperty1
# def area(self):
# return int(self.width * self.length)
#
# @property
# def area1(self):
# return int(self.width / self.length)
#
# r2 = Room1('卧室', 10, 10)
# print(r2.area1)
# print(r2.area) # 此时第一次运行会打印get
# print(r2.__dict__) # {'name': '卧室', 'width': 10, 'length': 10, 'area': 100}
# print(r2.area) # 第二次及往后的运行,就不会再打印get了,因为实例字典属性能找到,这样就不需要每次都运行函数进行计算了;如果描述符加了__set__成了数据描述符,结果又不一样了,因为优先级改变了


'''property补充'''
# class Foo:
# @property
# def ABC(self):
# print('get时候运行')
#
# @ABC.setter
# def ABC(self, value):
# print('set时候运行', value)
#
# @ABC.deleter
# def ABC(self):
# print('delete时候运行')

# 只有在属性ABC定义property后才能定义ABC.setter和ABC.deleter
# foo = Foo()
# foo.ABC
# foo.ABC = 'nnn' # 此时没有任何定义,报错AttributeError: can't set attribute
# foo.ABC = 'mmm'
# del foo.ABC


# 另一种写法
# class Foo1:
# def get_aaa(self):
# print('get')
#
# def set_aaa(self, value):
# print('set')
#
# def del_aaa(self):
# print('del')
#
# aaa = property(get_aaa, set_aaa, del_aaa) # 括号里顺序不能变,必须是get,set,del
#
# f1 = Foo1()
# f1.aaa
# f1.aaa = 'bcbc'
# del f1.aaa


'''property应用'''
# class Goods:
# def __init__(self):
# self.original_price = 100 # 原价
# self.discount = 0.8 # 折扣
#
# @property
# def price(self):
# new_price = self.original_price * self.discount # 打完折扣后的加个
# return new_price
#
# @price.setter
# def price(self, value): # 只能传入一个参数?
# self.original_price = value # 修改后的价格
#
# @price.deleter
# def price(self):
# del self.original_price
#
# g1 = Goods()
# print(g1.price) # 获取商品打完折扣后的价格80.0
# g1.price = 200
# print(g1.price) # 修改商品价格后的价格160.0
# del g1.price
# print(g1.price) # AttributeError: 'Goods' object has no attribute 'original_price'


# property实现类型检查
# class Typed:
# def __init__(self, name):
# self.name = name # 实例化就触发property
#
# @property
# def name(self):
# # return self.name # 如果只写一个property,property自动实现了set和get方法属于数据描述符,比实例属性优先级高,所以会触发property内置的set,抛出异常
# return self.doubi
#
# @name.setter
# def name(self, value):
# if not isinstance(value, str): # 必须是字符串类型
# raise TypeError('输入类型错误')
# self.doubi = value
#
# @name.deleter
# def name(self):
# del self.doubi
#
# t1 = Typed('alex')
# t2 = Typed(111)
上一篇:BAT大厂软件工程师 3分钟带你看懂android的Binder机制,必看


下一篇:html5基础---h5特性