一、属性读取
1.1、getter和setter
这里可以复写两个方法:__getattr__只在调用不存在的属性时才会被调用,__getattribute__无条件被调用,如果同时重写了以上两个方法,则__getattr__永远不会被调用。另外__getattr__对于双下划线开始和结尾、单下划线开始的方法是不能用的。
"""
如果自定义这两个方法,则
__getattr__只在调用不存在的属性时才会被调用
__getattribute__无条件被调用
如果同时重写了以上两个方法,则__getattr__永远不会被调用
"""
class A(object):
def __init__(self, name):
self.name = name
a = A('attribute')
print(a.name)
#attribute
class A1(object):
def __init__(self, name):
self.name = name
#用这种方法可以实现更友好的程序和属性隐藏,
def __getattr__(self, name):
print(f'calling __getattr__: {name}')
a = A1('attribute')
print(a.name)
print(a.test)
# attribute
# calling __getattr__: test
# None
class A1(object):
def __init__(self, name):
self.name = name
def __getattr__(self, name):
print(f'calling __getattr__: {name}')
def __getattribute__(self, item):
print("call getattribut")
a = A1('attribute')
print(a.name)
print(a.test)
# call getattribut
# None //这行是因为不存在属性所以打印出来的,因为覆写了__getattribute__方法
# call getattribut
# None //这行是因为不存在属性所以打印出来的
1.2、proxy
1.2.1、简单实现
class A(object):
def spam(self, x):
pass
def foo(self):
pass
class B(object):
"""
简单的代理
"""
def __init__(self):
self._a = A()
def spam(self, x):
return self._a.spam(x)
def foo(self):
return self._a.foo()
def bar(self):
pass
设置一个代理类,这个类只能代理被代理的public方法
# A proxy class that wraps around another object, but exposes its public attributes
class Proxy(object):
def __init__(self, obj):
self._obj = obj
# Delegate attribute lookup to internal obj
def __getattr__(self, name):
print(f'getattr: {name}')
return getattr(self._obj, name)
# Delegate attribute assignment
def __setattr__(self, name, value):
if name.startswith('_'):
super().__setattr__(name, value)
else:
print(f'setattr: {name} {value}')
setattr(self._obj, name, value)
# Delegate attribute deletion
def __delattr__(self, name):
if name.startswith('_'):
super().__delattr__(name)
else:
print(f'delattr: {name}')
delattr(self._obj, name)
class Spam(object):
def __init__(self, x):
self.x = x
def bar(self, y):
print(f'Spam.bar: {self.x}, {y}')
# Create an instance
s = Spam(2)
# # Create a proxy around it
p = Proxy(s)
# Access the proxy
p.bar(3) #Spam.bar: 2, 3
二、属性创建
2.2、属性的创建
class Person:
def __init__(self, first_name):
self.first_name = first_name #此处的first_name不带下划线的原因是,当创建属性时会自动调用 @first_name.setter标注的方法,实现检查的目的
# Getter function
def first_name(self):
return self._first_name
# Setter function
.setter
def first_name(self, value):
if not isinstance(value, str):
raise TypeError('Expected a string')
self._first_name = value
# Deleter function (optional)
.deleter
def first_name(self):
raise AttributeError("Can't delete attribute")
per = Person('Guido')
# Calls the getter
print(f'first name is: {per.first_name}')
# Calls the setter,Err, expected a string
per.first_name = 30
# Calls the deleter, Err, raise ErrorException
del per.first_name
2.2、在已有get/set方法的基础上创建property
name = property(get_first_name, set_first_name, del_first_name)
class Person:
def __init__(self, first_name):
self.set_first_name(first_name)
# Getter function
def get_first_name(self):
return self._first_name
# Setter function
def set_first_name(self, value):
if not isinstance(value, str):
raise TypeError('Expected a string')
self._first_name = value
# Deleter function (optional)
def del_first_name(self):
raise AttributeError("Can't delete attribute")
# Make a property from existing get/set methods
name = property(get_first_name, set_first_name, del_first_name)
2.3、动态计算属性
import math
class Circle:
def __init__(self, radius):
self.radius = radius
def area(self):
return math.pi * self.radius ** 2
def diameter(self):
return self.radius * 2
def perimeter(self):
return 2 * math.pi * self.radius
c = Circle(4.0)
print(f'radius is: {c.radius}')
# Notice lack of ()
print(f'area is: {c.area}')
print(f'perimeter is: {c.perimeter}')
2.4、延迟计算
下面这个__get__方法会早于Circle中的具体方法先行运算。
class LazyProperty:
def __init__(self, func):
self.func = func
def __get__(self, instance, cls):
if instance is None:
return self
else:
value = self.func(instance)
#下面这行可有可无,其中name指要调用的特定方法
setattr(instance, self.func.__name__, value)
return value
import math
class Circle:
def __init__(self, radius):
self.radius = radius
def area(self):
print('Computing area')
return math.pi * self.radius ** 2
def perimeter(self):
print('Computing perimeter')
return 2 * math.pi * self.radius
circle = Circle(6.0)
print(f'radius = {circle.radius}')
print(f'area = {circle.area}')
print(f'perimeter = {circle.perimeter}')
三、类型检查
3.1、通用方法
# Base class. Uses a descriptor to set a value
class Descriptor:
def __init__(self, name=None, **opts):
self.name = name
for key, value in opts.items():
setattr(self, key, value)
def __set__(self, instance, value):
instance.__dict__[self.name] = value
# Descriptor for enforcing types
class Typed(Descriptor):
expected_type = type(None)
def __set__(self, instance, value):
if not isinstance(value, self.expected_type):
raise TypeError(f'expected {str(self.expected_type)}')
super().__set__(instance, value)
# Descriptor for enforcing values
class Unsigned(Descriptor):
def __set__(self, instance, value):
if value < 0:
raise ValueError('Expected >= 0')
super().__set__(instance, value)
class Integer(Typed):
expected_type = int
class String(Typed):
expected_type = str
class SizedString(String):
pass
class Course:
# Specify constraints
course_name = SizedString('course_name')
total_class = Integer('total_class')
score = String('score')
def __init__(self, course_name, total_class, score):
self.course_name = course_name
self.total_class = total_class
self.score = score
course = Course('python', 30, "hello")
course.score = 'hello'
3.2、注解实现
# Class decorator to apply constraints
def check_attributes(**kwargs):
def decorate(cls):
for key, value in kwargs.items():
if isinstance(value, Descriptor):
value.name = key
setattr(cls, key, value)
else:
setattr(cls, key, value(key))
return cls
return decorate
# Example
(course_name=SizedString(size=8),
total_class=String,
score=String)
class Course:
def __init__(self, course_name, total_class, score):
self.course_name = course_name
self.total_class = total_class
self.score = score