2025-09-10:仓库迁移
This commit is contained in:
23
8.类与对象/1.修改实例的字符串表示.py
Normal file
23
8.类与对象/1.修改实例的字符串表示.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# 想要修改实例的字符串表示,可以在__str__和__repr__方法里做实现
|
||||
class Pair:
|
||||
def __init__(self,a,b):
|
||||
self.a = a
|
||||
self.b = b
|
||||
|
||||
def __repr__(self):
|
||||
return 'Pair({0.a}, {0.b})'.format(self)
|
||||
|
||||
def __str__(self):
|
||||
return '({0.a}, {0.b})'.format(self)
|
||||
|
||||
# format格式复习: {元组下标:填充元素 填充方法(<^>) 填充长度 数据格式(+-.nf d e % 等)}
|
||||
|
||||
# 在下面的示例中,!r表示使用__repr__做输出,!s表示使用__str__做输出,这样就不用担心!s和!r顶掉数据格式化字符串的位置了
|
||||
p =Pair(3, 4)
|
||||
print("P is {!r}".format(p))
|
||||
print("P is {!s}".format(p))
|
||||
|
||||
# 如果输出不太妙,或者没有输出的方法,就会用<>返回你一段文本,比如文件的返回
|
||||
path = "5.文件与IO/1.somefile.txt"
|
||||
f = open(path,"r")
|
||||
print(f)
|
38
8.类与对象/10.让属性具有惰性求值能力.py
Normal file
38
8.类与对象/10.让属性具有惰性求值能力.py
Normal file
@@ -0,0 +1,38 @@
|
||||
# 在之前的课程中,我们学会了给类的属性加property来让它们惰性求值
|
||||
# 但是我们想要它求了一次以后就把值储存起来,下次调用就可以节省算力了
|
||||
# 我们可以使用描述符类来完成这个操作
|
||||
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)
|
||||
# 将原来类中的方法覆写删除,变成固定的数字
|
||||
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):
|
||||
return 2 * self.radius
|
||||
|
||||
|
||||
c = Circle(4)
|
||||
print(c.area)
|
||||
print(c.area)
|
||||
|
||||
# 这样相当于将实例中的方法替换为某个可替换的值,代价就是属性不再由计算得出,仅作为数据读取
|
||||
# 这种方法可以用在一些只计算一次的实例上,来节省计算
|
59
8.类与对象/11.简化数据结构的初始化过程.py
Normal file
59
8.类与对象/11.简化数据结构的初始化过程.py
Normal file
@@ -0,0 +1,59 @@
|
||||
# 有时候你编写了一堆类当数据结构用,但是懒得写一堆init函数,这时候可以将初始化数据结构的init函数归纳到一个公共基类中
|
||||
|
||||
class Structure:
|
||||
_fields = []
|
||||
def __init__(self, *args, **kwargs):
|
||||
if len(args) > len(self._fields):
|
||||
raise TypeError("Expect {} arguments, but only {}".format(len(self._fields), len(args)))
|
||||
|
||||
# 处理顺序输入的参数
|
||||
for name, value in zip(self._fields, args):
|
||||
setattr(self, name, value)
|
||||
|
||||
# 处理关键字参数
|
||||
for name in args[len(self._fields):]:
|
||||
setattr(self, name, kwargs.pop(name))
|
||||
|
||||
# 处理多余的关键字参数
|
||||
if kwargs:
|
||||
raise TypeError("Invalid arguments {}".format(",".join(kwargs)))
|
||||
|
||||
|
||||
class lazy:
|
||||
def __init__(self, func):
|
||||
self.func = func
|
||||
|
||||
def __get__(self, instance, cls):
|
||||
if instance is None:
|
||||
return self
|
||||
else:
|
||||
value = self.func(instance)
|
||||
setattr(instance, self.func.__name__, value)
|
||||
return value
|
||||
|
||||
# 这时候我们会发现你只需要指定参数就行了,异常的好使
|
||||
import math
|
||||
if __name__ == '__main__':
|
||||
|
||||
class Stock(Structure):
|
||||
_fields = ["name", "shares", "price"]
|
||||
__slots__ = _fields
|
||||
|
||||
class Points(Structure):
|
||||
_fields = ["x", "y"]
|
||||
__slots__ = _fields
|
||||
|
||||
|
||||
class Circle(Structure):
|
||||
_fields = ["radius"]
|
||||
__slots__ = _fields
|
||||
@lazy
|
||||
def area(self):
|
||||
return math.pi * self.radius ** 2
|
||||
|
||||
s = Stock('ACME', 50, 91.1)
|
||||
p = Points(2,3)
|
||||
c = Circle(4.5)
|
||||
print(c.area)
|
||||
print(c.area)
|
||||
|
26
8.类与对象/2.自定义字符串的输出格式.py
Normal file
26
8.类与对象/2.自定义字符串的输出格式.py
Normal file
@@ -0,0 +1,26 @@
|
||||
_formats = {
|
||||
'ymd': '{d.year}-{d.month}-{d.day}',
|
||||
'mdy': '{d.month}-{d.day}-{d.year}',
|
||||
'dym': '{d.day}-{d.month}-{d.year}'
|
||||
}
|
||||
|
||||
# 通过重写类的__format__方法,我们可以让类在被填充的时候变成任何样子
|
||||
class Date:
|
||||
def __init__(self, year, month, day):
|
||||
self.year = year
|
||||
self.month = month
|
||||
self.day = day
|
||||
|
||||
def __format__(self, code):
|
||||
if code == '':
|
||||
code = 'ymd'
|
||||
fmt = _formats[code]
|
||||
return fmt.format(d=self)
|
||||
|
||||
d = Date(2024, 11, 11)
|
||||
format(d)
|
||||
format(d, 'mdy')
|
||||
|
||||
"The Date is {:ymd}".format(d)
|
||||
"The Date is {:dym}".format(d)
|
||||
|
58
8.类与对象/3.让对象支持上下文管理协议.py
Normal file
58
8.类与对象/3.让对象支持上下文管理协议.py
Normal file
@@ -0,0 +1,58 @@
|
||||
# 有时候我们想让类支持with触发的上下文管理协议实现自动启动和关闭
|
||||
# 这时候就要实现类的__enter__和__exit__方法
|
||||
|
||||
from socket import socket, AF_INET, SOCK_STREAM
|
||||
# 比如下面这个网络连接类
|
||||
class LazyConnection:
|
||||
def __init__(self, address, family=AF_INET, type=SOCK_STREAM):
|
||||
self.address = address
|
||||
self.family = family
|
||||
self.type = type
|
||||
self.sock = None
|
||||
|
||||
def __enter__(self):
|
||||
if self.sock is not None:
|
||||
raise RuntimeError
|
||||
self.sock = socket(self.family, self.type)
|
||||
self.sock.connect(self.address)
|
||||
print("链接被拉起")
|
||||
return self.sock
|
||||
|
||||
def __exit__(self, exc_ty, exc_va, tb):
|
||||
self.sock.close()
|
||||
self.sock = None
|
||||
print("链接关闭")
|
||||
|
||||
from functools import partial
|
||||
conn = LazyConnection(('www.python.org', 80))
|
||||
with conn as s:
|
||||
# 链接被拉起
|
||||
s.send(b'GET /index.html HTTP/1.0\r\n')
|
||||
s.send(b'Host: www.python.org\r\n')
|
||||
s.send(b'\r\n')
|
||||
resp = b''.join(iter(partial(s.recv, 1024), b''))
|
||||
# 链接被关闭
|
||||
|
||||
# 但是这个类只能使用单层的with,如果有多个with嵌套,它就会失效
|
||||
# 可以对原来的类进行一次修改,做一个工厂类,每次从一个栈中存取一个链接
|
||||
class LazyConnectionV2():
|
||||
def __init__(self, address, family=AF_INET, type=SOCK_STREAM):
|
||||
self.address = address
|
||||
self.family = family
|
||||
self.type = type
|
||||
# 单个的sock被替换成空列表
|
||||
self.connections = []
|
||||
|
||||
def __enter__(self):
|
||||
# 建立一个socket链接
|
||||
sock = socket(self.family, self.type)
|
||||
sock.connect(self.address)
|
||||
# 将链接压入栈中
|
||||
self.connections.append(sock)
|
||||
print("链接被拉起")
|
||||
return sock
|
||||
|
||||
def __exit__(self, exc_ty, exc_va, tb):
|
||||
# 将最近被压入栈的链接弹出并关闭
|
||||
self.connections.pop().close()
|
||||
print("链接关闭")
|
11
8.类与对象/4.创建大量实例时如何节省内存.py
Normal file
11
8.类与对象/4.创建大量实例时如何节省内存.py
Normal file
@@ -0,0 +1,11 @@
|
||||
# 如果你需要创建大量的实例,那么你可以在类的定义中增加__slots__属性来让类不创建__dict__字典
|
||||
|
||||
class Date:
|
||||
__slots__ = ('year', 'month', 'day')
|
||||
def __init__(self, year, month, day):
|
||||
self.year = year
|
||||
self.month = month
|
||||
self.day = day
|
||||
|
||||
# 这会让类围绕着__slots__属性进行构建,副作用就是这个类被写死了,我们无法为它的实例添加新的属性
|
||||
# 使用了__slots__的类会失去多重继承的用法,请确保只在被当作数据结构的类中使用该方法
|
46
8.类与对象/5.将名称封装到类中.py
Normal file
46
8.类与对象/5.将名称封装到类中.py
Normal file
@@ -0,0 +1,46 @@
|
||||
# 有时候我们需要一些private属性,但是python在类中并没有做访问控制
|
||||
# 我们遵循一些基本的编程规则来让大家达成共识
|
||||
# 1.别碰单下划线开头的东西
|
||||
class A:
|
||||
def __init__(self):
|
||||
self._internal=0
|
||||
self.public = 1
|
||||
|
||||
def public_method(self):
|
||||
print("It's a public method")
|
||||
|
||||
def _private_method(self):
|
||||
print("It's a private method")
|
||||
|
||||
# 这是人为制约的标准,请务必遵守它,访问_变量的行为是粗鲁的
|
||||
# 如果你想要让某个方法在继承时不被覆写,那么可以使用__
|
||||
# 在继承时,__开头的方法会被重整成_类名__方法名的形式
|
||||
|
||||
class B:
|
||||
def __init__(self):
|
||||
self.__private=0
|
||||
|
||||
def __private_method(self):
|
||||
print("这是B的私有方法")
|
||||
|
||||
def public_method(self):
|
||||
print("这是B的公有方法")
|
||||
|
||||
class C(B):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.__private = 1
|
||||
|
||||
# 重新定义C的方法
|
||||
def __private_method(self):
|
||||
print("这是C的私有方法")
|
||||
|
||||
# 可以看到B的私有方法没有被覆写,而是被重整成了_B__private_method
|
||||
def public_method(self):
|
||||
self.__private_method()
|
||||
super()._B__private_method()
|
||||
c = C()
|
||||
c.public_method()
|
||||
|
||||
# 总结,如果只是私有名称,那么可以使用_;如果私有名称还需要对子类隐藏,那就使用__
|
||||
# 在与保留关键字冲突时,可以使用 命名_ 的方式来避免名称冲突同时与私有数据区分开
|
104
8.类与对象/6.创建可管理的属性.py
Normal file
104
8.类与对象/6.创建可管理的属性.py
Normal file
@@ -0,0 +1,104 @@
|
||||
# 如果需要自定义对属性的访问,最好的办法就是将属性定义为一个property
|
||||
|
||||
# 在这个类中,我们将first_name的getter方法作为一个property
|
||||
# 在初始化调用时,其实调用了setter方法,跳过了first_name直接访问了_first_name
|
||||
class Person:
|
||||
def __init__(self, first_name):
|
||||
self.first_name = first_name
|
||||
|
||||
@property
|
||||
def first_name(self):
|
||||
return self._first_name
|
||||
|
||||
@first_name.setter
|
||||
def first_name(self, first_name):
|
||||
if not isinstance(first_name, str):
|
||||
raise TypeError('First name must be str')
|
||||
self._first_name = first_name
|
||||
|
||||
@first_name.deleter
|
||||
def first_name(self):
|
||||
raise AttributeError('First name cannot be deleted')
|
||||
|
||||
# property的重要特性就是根据访问的不同形式,自动触发getter setter deleter
|
||||
p = Person("Susan")
|
||||
print(p.first_name)
|
||||
p.first_name = "blyet"
|
||||
print(p.first_name)
|
||||
# del p.first_name
|
||||
|
||||
# 对于已经存在的get和set方法,也可以使用property变成内置形式
|
||||
class Person2:
|
||||
def __init__(self, first_name):
|
||||
self.set_first_name(first_name)
|
||||
|
||||
def get_first_name(self):
|
||||
return self._first_name
|
||||
|
||||
def set_first_name(self, first_name):
|
||||
if not isinstance(first_name, str):
|
||||
raise TypeError('First name must be str')
|
||||
self._first_name = first_name
|
||||
|
||||
def delete_first_name(self):
|
||||
raise AttributeError('First name cannot be deleted')
|
||||
|
||||
name = property(get_first_name, set_first_name, delete_first_name)
|
||||
|
||||
p = Person2("Susan")
|
||||
print(p.name)
|
||||
p.name = "blyet"
|
||||
print(p.name)
|
||||
# del p.name
|
||||
|
||||
# 这种方法一般用于确定要在访问属性的时候对它进行一些额外操作的时候,其他场景下尽量不要这么做,这会让程序变慢
|
||||
|
||||
# property也可以用来做一些类似懒加载的实时计算属性
|
||||
import math
|
||||
class Circle:
|
||||
def __init__(self, radius):
|
||||
self.radius = radius
|
||||
|
||||
@property
|
||||
def area(self):
|
||||
return math.pi * self.radius ** 2
|
||||
|
||||
@property
|
||||
def perimeter(self):
|
||||
return 2 * self.radius
|
||||
|
||||
c = Circle(5)
|
||||
print(c.radius)
|
||||
print(c.area)
|
||||
print(c.perimeter)
|
||||
|
||||
# 虽然这样确实很优雅,但是在Python被大型程序集成的时候,还是使用传统getter和setter好
|
||||
# 另外,不要在类里写大量的property!!!!,这会让代码膨胀且可读性变差,会变得难以维护,像下面这样
|
||||
class Person3:
|
||||
def __init__(self, first_name, last_name):
|
||||
self.first_name = first_name
|
||||
self.last_name = last_name
|
||||
|
||||
@property
|
||||
def first_name(self):
|
||||
return self._first_name
|
||||
|
||||
@first_name.setter
|
||||
def first_name(self, first_name):
|
||||
if not isinstance(first_name, str):
|
||||
raise TypeError('First name must be str')
|
||||
|
||||
self._first_name = first_name
|
||||
|
||||
# 不要这样重复写property!
|
||||
@property
|
||||
def last_name(self):
|
||||
return self._last_name
|
||||
|
||||
@last_name.setter
|
||||
def last_name(self, last_name):
|
||||
if not isinstance(last_name, str):
|
||||
raise TypeError('Last name must be str')
|
||||
self._last_name = last_name
|
||||
|
||||
|
119
8.类与对象/7.调用父类中的方法.py
Normal file
119
8.类与对象/7.调用父类中的方法.py
Normal file
@@ -0,0 +1,119 @@
|
||||
# 要调用父类中的方法,可以使用super完成
|
||||
class A:
|
||||
def spam(self):
|
||||
print('A.spam')
|
||||
|
||||
class B(A):
|
||||
def spam(self):
|
||||
print('B.spam')
|
||||
super().spam()
|
||||
|
||||
# super的另一种常用方法是调用父类的__init__方法来确保父类被正确初始化了
|
||||
class A:
|
||||
def __init__(self):
|
||||
self.x = 0
|
||||
|
||||
class B(A):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.y = 1
|
||||
|
||||
# 还有一种情况就是,你覆写了类中的方法,此时你可以调用super函数来使用原方法
|
||||
class Proxy:
|
||||
def __init__(self, obj):
|
||||
self._obj = obj
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self._obj, name)
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
if name.startwith('_'):
|
||||
super().__setattr__(name, value)
|
||||
else:
|
||||
setattr(self._obj, name, value)
|
||||
|
||||
# Python中的类继承
|
||||
# 有些人有坏习惯,在子类中直接调用父类的方法,这在大部分时候能行
|
||||
class Base:
|
||||
def __init__(self):
|
||||
print('Base.__init__')
|
||||
|
||||
class A(Base):
|
||||
def __init__(self):
|
||||
Base.__init__(self)
|
||||
print('A.__init__')
|
||||
|
||||
# 但是如果这涉及到多重继承,那就会导致暴毙现象
|
||||
class BaseV2:
|
||||
def __init__(self):
|
||||
print('Base.__init__')
|
||||
|
||||
class A1(BaseV2):
|
||||
def __init__(self):
|
||||
BaseV2.__init__(self)
|
||||
print('A1.__init__')
|
||||
|
||||
class A2(BaseV2):
|
||||
def __init__(self):
|
||||
BaseV2.__init__(self)
|
||||
print('A2.__init__')
|
||||
|
||||
class B1(A1, A2):
|
||||
def __init__(self):
|
||||
A1.__init__(self)
|
||||
A2.__init__(self)
|
||||
print('B1.__init__')
|
||||
|
||||
# 上面的继承关系就是标准的钻石继承,如果运行代码就会出现神奇魔法
|
||||
b1 = B1()
|
||||
# 哇!Base的__init__函数被调用了两次!这在一些场景下可能会导致麻烦,但如果使用super(),那就不会有这么多事了
|
||||
|
||||
class BaseV3:
|
||||
def __init__(self):
|
||||
print('Base.__init__')
|
||||
|
||||
class B1(BaseV3):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
print('B1.__init__')
|
||||
|
||||
class B2(BaseV3):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
print('B2.__init__')
|
||||
|
||||
class C1(B1, B2):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
print('C1.__init__')
|
||||
# 这样的继承是健康的,每个init函数只被调用了一次
|
||||
c1 = C1()
|
||||
|
||||
# 这是为啥嘞?每个类Python都会计算得出一个方法解析顺序列表MRO,在这个列表中会简单的对所有基类进行线性排列
|
||||
print(C1.__mro__)
|
||||
# MRO列表又是怎么确定的呢?Python使用了一种叫C3线性化处理的技术,这是一种归并排序,且满足三个约束
|
||||
# 1.先检查子类再检查父类; 2.多个父类时按照列表顺序依次检查; 3.如果下一个待选的类出现了两个合法的选择,就从第一个父类中选取
|
||||
# C3有点麻烦,但是计算的时候可以使用Python2.2更新的新式类MRO方法:
|
||||
# 从左至右的深度优先遍历,但是如果遍历中出现重复的类,只保留最后一个,之后再根据去重前类的出现顺序排序
|
||||
|
||||
# 使用super函数时,会从MRO列表中的下一个类中搜索方法,只要每一个重定义过的方法都使用了super(),super就能保证让这个方法只被调用一次
|
||||
# super(a, b)函数的本质是获取类a在类b的MRO列表中a的下一个类
|
||||
# 但是这样也可能出现以下意想不到的孝bug
|
||||
class D:
|
||||
def spam(self):
|
||||
print('D.spam')
|
||||
super().spam()
|
||||
|
||||
class D1:
|
||||
def spam(self):
|
||||
print('D1.spam')
|
||||
|
||||
class D2(D, D1):
|
||||
pass
|
||||
|
||||
d = D2()
|
||||
d.spam()
|
||||
|
||||
# 这是由于MRO列表导致的super函数bug,这三个类的MRO列表应该是[D2, D, D1], super去找了D1中的方法
|
||||
# 要避免遇到麻烦,最好遵守下面两点
|
||||
# 1.确保所有的类中都实现了相同签名的调用; 2.确保你要调用的方法在顶层被实现(例子中为D2)
|
89
8.类与对象/8.在子类中扩展属性.py
Normal file
89
8.类与对象/8.在子类中扩展属性.py
Normal file
@@ -0,0 +1,89 @@
|
||||
# 我们有时候想在子类中定义一些父类没有的属性进行属性拓展
|
||||
class Person:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@name.setter
|
||||
def name(self, name):
|
||||
if not isinstance(name, str):
|
||||
raise TypeError('name must be str')
|
||||
else:
|
||||
self._name = name
|
||||
|
||||
@name.deleter
|
||||
def name(self):
|
||||
raise AttributeError('name cannot be deleted')
|
||||
|
||||
# 我们从Person中继承,并对name属性的功能进行拓展
|
||||
class SubPerson(Person):
|
||||
@property
|
||||
def name(self):
|
||||
print("Getting name...")
|
||||
return super().name
|
||||
|
||||
@name.setter
|
||||
def name(self, value):
|
||||
print("Setting name to {}...".format(value))
|
||||
super(SubPerson, SubPerson).name.__set__(self, value)
|
||||
|
||||
@name.deleter
|
||||
def name(self):
|
||||
print("Deleting name...")
|
||||
super(SubPerson, SubPerson).name.__delete__(self)
|
||||
|
||||
# s = SubPerson("Guido")
|
||||
# s.name
|
||||
# s.name = "Larry"
|
||||
# s.name = 42
|
||||
|
||||
|
||||
# 如果只是想拓展属性的其中一个方法,可以使用如下代码实现
|
||||
# 只修改setter
|
||||
class SubPerson2(Person):
|
||||
# 指定父类中属性的某种方法
|
||||
@Person.name.setter
|
||||
def name(self, value):
|
||||
print("Setting name to {}...".format(value))
|
||||
super(SubPerson2, SubPerson2).name.__set__(self, value)
|
||||
|
||||
s2 = SubPerson2("Guido2")
|
||||
s2.name = "Larry"
|
||||
|
||||
#只修改getter
|
||||
class SubPerson3(Person):
|
||||
# 我们也可以这样操作
|
||||
@Person.name.getter
|
||||
def name(self):
|
||||
print('Getting name .....')
|
||||
return super().name
|
||||
|
||||
s3 = SubPerson3("Guido3")
|
||||
s3.name
|
||||
s3.name = "Larry"
|
||||
s3.name
|
||||
|
||||
# Python3让我们太舒服了,super的本质是super(类名, self),这些参数被省略了,它的意思其实是从self的MRO列表里寻找类名的下一个位置
|
||||
# 所以这里的 super(SubPerson, SubPerson).name 其实是在SubPerson的MRO列表中寻找SubPerson的下一个类,用途是找到Person
|
||||
|
||||
# 万万记住,这种方式一次只能修改一个方法,如果一次修改多个,生效的只有最后被定义的那个
|
||||
#比如这里生效的只有setter
|
||||
class SubPerson4(Person):
|
||||
# 我们也可以这样操作
|
||||
@Person.name.getter
|
||||
def name(self):
|
||||
print('Getting name .....')
|
||||
return super(SubPerson4, SubPerson4).name.__get__(self)
|
||||
|
||||
@Person.name.setter
|
||||
def name(self, value):
|
||||
print("Setting name to {}...".format(value))
|
||||
super(SubPerson4, SubPerson4).name.__set__(self, value)
|
||||
|
||||
s4 = SubPerson4("Guido4")
|
||||
s4.name
|
||||
s4.name = "Larry"
|
||||
s4.name
|
65
8.类与对象/9.创建一种新形式的类属性或实例属性.py
Normal file
65
8.类与对象/9.创建一种新形式的类属性或实例属性.py
Normal file
@@ -0,0 +1,65 @@
|
||||
# 如果想要创建一个新形式的实例属性,可以以描述符类的形式定义其功能
|
||||
# 所谓描述符就是以特殊方法__get__; __set__; __delete__的形式实现了三个核心属性的访问操作
|
||||
class Integer:
|
||||
# 存储名称
|
||||
def __init__(self,name):
|
||||
self.name = name
|
||||
|
||||
# 如果没有输入目标对象,就返回自身,如果输入了获取目标对象的__dict__属性里key是name的条目
|
||||
def __get__(self, instance, cls):
|
||||
# 这里的描述比较复杂
|
||||
# ,如果instance是空的,说明访问的是类,那么我们就要返回类本身
|
||||
# 如果instance不是空的,那说明是实例对象,需要返回实例的字段
|
||||
if instance is None:
|
||||
return self
|
||||
else:
|
||||
return instance.__dict__[self.name]
|
||||
|
||||
# 如果设置输入不是整数,就报错.否则往目标对象的__dict__属性中写入{name: value}
|
||||
def __set__(self, instance, value):
|
||||
if not isinstance(value, int):
|
||||
raise TypeError('value must be an integer')
|
||||
print("Set {} to {}".format(self.name, value))
|
||||
instance.__dict__[self.name] = value
|
||||
|
||||
# 将key为name的属性从目标对象的__dict__中移除
|
||||
def __delete__(self, instance):
|
||||
del instance.__dict__[self.name]
|
||||
|
||||
class Point:
|
||||
# 先设置x和y(其实是初始化self.x和self.y)
|
||||
x = Integer('x')
|
||||
y = Integer('y')
|
||||
|
||||
# 实例化的时候调用
|
||||
def __init__(self,x,y):
|
||||
# 调用Integer的__set__方法来给元素赋值
|
||||
self.x = x
|
||||
self.y = y
|
||||
|
||||
p = Point(2,3)
|
||||
p.x
|
||||
p.y
|
||||
# 很明显,如果我首先初始化一个b,然后对b使用__set__方法指定p作为插入对象,那这个类就有麻烦了
|
||||
b = Integer('b')
|
||||
print("\n{:-^18}".format("这里有脏东西"))
|
||||
b.__set__(p, 3)
|
||||
print("{:-^18}\n".format("这里有脏东西"))
|
||||
# 这说明一件事,如果对实例的__dict__属性进行操作,那么我们就可以搞到一个动态类
|
||||
|
||||
# 初始化一定要在实例化之前进行,否则就只能用拙劣的__set__方法了,这就比较丑陋了
|
||||
class Point:
|
||||
# 实例化的时候调用
|
||||
def __init__(self, x, y):
|
||||
# 先设置x和y
|
||||
self.x = Integer('x')
|
||||
self.y = Integer('y')
|
||||
# 调用Integer的__set__方法来给元素赋值
|
||||
self.x.__set__(self, x)
|
||||
self.y.__set__(self, y)
|
||||
|
||||
p = Point(2,3)
|
||||
p.x
|
||||
p.y
|
||||
|
||||
# 当然,这样做会很爽,但如果你只需要给某个特定类中的一种属性加东西,最好还是使用property重写
|
Reference in New Issue
Block a user