描述符及类的装饰器应用实现类型检查,自定制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' # 此时没有定义ABC.setter,报错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): # 这个类属性和实例属性同名了,而且加了property,此property属于数据描述符,所以优先级大于实例属性
#     # 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)
while True: print('studying...')
原文地址:https://www.cnblogs.com/xuewei95/p/14760816.html