Python从门到精通(二):包装-05-类的属性(补充)
  TEZNKK3IfmPf 2024年03月29日 63 0

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
@property
def first_name(self):
return self._first_name

# Setter function
@first_name.setter
def first_name(self, value):
if not isinstance(value, str):
raise TypeError('Expected a string')
self._first_name = value

# Deleter function (optional)
@first_name.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

@property
def area(self):
return math.pi * self.radius ** 2

@property
def diameter(self):
return self.radius * 2

@property
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

@LazyProperty
def area(self):
print('Computing area')
return math.pi * self.radius ** 2

@LazyProperty
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
@check_attributes(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
【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2024年03月29日 0

暂无评论

推荐阅读
  TEZNKK3IfmPf   2024年05月31日   38   0   0 python开发语言
  TEZNKK3IfmPf   2024年05月31日   28   0   0 python
  TEZNKK3IfmPf   2024年05月31日   35   0   0 excelpython
  TEZNKK3IfmPf   2024年05月31日   30   0   0 python
TEZNKK3IfmPf