在Python中,数据的属性和处理数据的方法,统称属性。方法,只是可调用的属性。除了这两者之外,我们还可以创建特性(property),在不改变类接口的前提下,使用存取方法(即读值方法和设值方法)修改数据属性。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/3/13 20:56
# @Author : Maple
# @File : 00-相关知识点.py
# @Software: PyCharm
class Animal:
def __init__(self,name):
self.name = name
@property
def run(self):
print('Animal is running')
class Computer:
def __init__(self,brand,price):
self.brand = brand
self.price = price
@classmethod
def computing(cls):
print("I love computing")
if __name__ == '__main__':
# 1.更新字典的值
person= {"name":"Maple","age":19}
person.update({"name":"Max","gender":"Maple"})
print(person)
# 2.类的__dict__中存放着其属性
a = Animal('Dog')
## 初次查看a的属性
print(a.__dict__) # {'name': 'Dog'}
## 通过更新__dict__,可以给类增加属性
a.__dict__.update({"age":10})
## 查看更新后的a包含的属性
print(a.__dict__) # {'name': 'Dog', 'age': 10}
# 3.以访问属性的方式,调用方法
## 没有添加 property装饰器之前,通过如下方式调用run方法
#a.run() # Animal is running
## 添加property装饰器之后,可以以引用属性的方式调用run方法
a.run # Animal is running
# 4.类相关的一些内置属性
print('******4.类相关的一些内置属性********')
print(Animal) #
##4-1. 类的名称
print(Animal.__name__)
##4-2 类的类型:为type
print(Animal.__class__) #
print(Animal.__class__.__name__) # type
##4-3对比实例的__class__
print(a.__class__) #
print(Animal) #
print(Animal.run) #
print(a.__class__.run) #
# 5. 通过实例调用类方法
com = Computer('Apple', 20000)
com.computing() # I love computing
# 6.直接通过类调用类方法
Computer.computing() # I love computing
com.__class__.computing() # I love computing
# 6.从模块的全局作用域中获取对象(包括类),如果获取不到给默认值
## 获取Animal的一个实例对象
print(globals().get('a', Computer)) # <__main__.Animal object at 0x000001D5FB513DC0>
## 获取Animal类
print(globals().get('Animal')) #
print(globals().get('Cat',Computer)) #
cc = globals().get('Cat', Computer)
print(issubclass(cc,Computer)) #True
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/3/12 21:17
# @Author : Maple
# @File : 01-使用动态属性访问Json数据(1).py
# @Software: PyCharm
from collections import abc
from osconfeed import load
class FrozenJSON:
def __init__(self,mapping):
self.__data = dict(mapping)
def __getattr__(self, item):
if hasattr(self.__data,item):
return self.__data[item]
else:
return FrozenJSON.build(self.__data[item])
@classmethod
def build(cls,obj):
if isinstance(obj,abc.Mapping):
# 如果是一个键值对类型,就直接返回一个FrozenJSON对象
return cls(obj)
# 如果是一个列表,就为列表中的每个元素创建一个FrozenJSON对象(其实是将JSON中的对象转换成FrozenJSON对象)
elif isinstance(obj,abc.MutableSequence):
return [cls(item) for item in obj]
# 否则就返回对象本身
else:return obj
if __name__ == '__main__':
# 1. 获取原始Json对象:实际已转换为Python中的对象字典
osconfeed = load()
print(type(osconfeed)) #
#2.JSON对象封装成 FrozenJSON
print('**** 2-1.封装osconfeed**********')
## 2-1.此时f_json仅有一个属性__data,对应的values是osconfeed.json(转成字典格式)
f_json = FrozenJSON(osconfeed)
print('f_json:', f_json.__dict__)
## 2-2 访问f_json.schedule,因为f_json中并没有schedule属性
print('**** 2-2.访问schedule**********')
# 因此会调用 FrozenJSON.build(self.__data[item]),会返回一个FrozenJSON实例对象,且该实例属性名依然为__data,对应的value
# 则是原json数据中schedule下对应的value
f_json_schedule = f_json.schedule
print('f_json_schedule:',f_json_schedule.__dict__)
# print(f_json.schedule.conference[0].serial) # 115
## 2-3 继续访问schedule下的events,由于f_json_schedule中并没有events属性:
# 因此会调用 FrozenJSON.build(self.__data[item]),由于events下是一个list,因此该方法也会返回一个FrozenJSON对象列表
print('**** 2-3.访问schedule.events**********')
FrozenJSON_events_list = f_json_schedule.events
for i,frozen_event in enumerate(FrozenJSON_events_list):
# frozen_event仍然没有serial属性,因此会调用FrozenJSON.build(self.__data[item])
# 然后 因为self.__data['serial']对应的值既不是Mapping又不是MutableSequence,因此返回值本身:即数字
print('**2-4.访问schedule.events下的serial****')
result = frozen_event.serial
print('第{}个frozen_event下的serial对应的值是{}'.format(i,result))
(1) osconfeed.json数据
{
"schedule": {
"conference":[{"serial":115}],
"events":[
{"serial":4505,
"name": "Go to the island",
"event_type":"escape",
"venue_serial": 1462,
"speakers": [8890,8891]
},
{"serial":4506,
"name": "To be my best",
"event_type":"study",
"venue_serial": 1463,
"speakers": [8890,8891]
}],
"speakers": [
{"serial":8890,
"name": "Jacky",
"age": 30
},
{"serial":8891,
"name": "Tom",
"age": 33
}
],
"venues":[
{
"serial":1462,
"name":"F151",
"category":"Conference Venues"
},
{
"serial":1463,
"name":"F152",
"category":"Super Man Venues"
}
]
}
}
(2) load方法
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/3/12 21:27
# @Author : Maple
# @File : osconfeed.py
# @Software: PyCharm
import json
JSON = r"D:\01-study\python\fluent_python\19-动态属性和特性\data\osconfeed.json"
def load():
with open(JSON) as f:
# 返回Python对象
return json.load(f)
if __name__ == '__main__':
r = load()
print(type(r)) #
print(r['schedule']['conference'])
print(r)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/3/16 20:30
# @Author : Maple
# @File : 01-使用动态属性访问Json数据(2).py
# @Software: PyCharm
"""改造FrozenJSON
使用__new__方法构造对象,代替build方法
"""
from collections import abc
from keyword import iskeyword
from osconfeed import load
class FrozenJSON:
def __new__(cls, arg):
if isinstance(arg,abc.Mapping):
return super().__new__(cls)
elif isinstance(arg,abc.MutableSequence):
return [cls(item) for item in arg]
else:
return arg
def __init__(self,mapping):
self.__data = {}
for key,value in mapping.items():
if iskeyword(key):
key +='_'
self.__data[key] = value
def __getattr__(self, name):
# 1-1如果实例中有某个对象,就直接返回属性对应的值
# 2-1 比如去访问schedule属性,发现并没有
""""
"schedule": {
"conference":[{"serial":115}],
...
"""
if hasattr(self.__data,name):
return self.__data[name]
else:
# 1-2否则就返回一个FrozenJSON对象
# 1-3以下语法会直接去调用FrozenJSON.__new__方法,构建对象实例
# 2-2 通过如下方式创建FrozenJSON对象,其中self.__data["schedule"]
return FrozenJSON(self.__data[name])
if __name__ == '__main__':
# 1.初始化
osconfeed = load()
"""初始化流程分析
1.首先走__new__方法,因为初始化参数是一个Mapping,所以会return super().__new__(cls),返回一个FrozenJSON实例化对象
2.实例化对象传递到__init__方法,对其进行初始化:首先会生成一个实例属性__data,其value为空{}
3.然后对其进行赋值,且其key为`schedule`,value为{'conference'.....}
4.最终的结果是f_json有一个__data实例属性,然后其值为 {'schedule': {'conference': [{'serial': 115}],.....}
"""
f_json = FrozenJSON(osconfeed)
print(f_json.__dict__)
# 2.访问f_json的schedule属性
"""访问schedule属性流程分析
1.由于f_json中并没有 schedule 属性(只有__data属性),所以会走__getattr__的else逻辑
2.通过FrozenJSON(self.__data[name])构造实例对象(走__new__和__init__逻辑),其中self.__data['schedule']为 {'conference': [{'serial': 115}],...}
3.因此返回的f_json_schedule会有一个__data属性.而且其值为 {'conference': [{'serial': 115}],...},具体来说key为conference,value为[{"serial":115}]
"""
f_json_schedule = f_json.schedule
print(f_json_schedule)
# 3.访问 f_json_schedule 的 conference 属性
"""访问schedule下的conference属性分析
1.由于 f_json_schedule 中并没有 conference 属性(只有__data属性),所以会走__getattr__的else逻辑
2.通过FrozenJSON(self.__data[name])构造实例对象列表(因为self.__data['conference']对应的value为list:[{'serial': 115}]
走__new__和__init__逻辑),其中self.__data['conference']为 [{'serial': 115}]
3.因此返回的f_json_schedule列表会有一个__data属性.而且其值为 {'serial': 115},具体来说key为 serial,value为115
"""
f_json_schedule_conference = f_json_schedule.conference
print(f_json_schedule_conference)
# 4. 访问f_json_schedule_conference属性的serial属性
"""访问schedule.conference下的serial属性分析
1.由于 f_json_schedule_conference 中并没有 serial 属性(只有__data属性),所以会走__getattr__的else逻辑
2.通过FrozenJSON(self.__data[name])返回serial对应的值(因为self.__data['serial']对应的value为115,会直接返回值本身
"""
result = f_json_schedule_conference[0].serial
print(result) # 115
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/3/13 21:05
# @Author : Maple
# @File : 03-使用shelve模块访问Json.py
# @Software: PyCharm
"""
schedule2.py: traversing OSCON schedule data
>>> import shelve
>>> db = shelve.open(DB_NAME)
>>> if CONFERENCE not in db: load_db(db)
# BEGIN SCHEDULE2_DEMO
>>> DbRecord.set_db(db) # <1>
>>> event = DbRecord.fetch('event.33950') # <2>
>>> event # <3>
>>> event.venue # <4>
>>> event.venue.name # <5>
'Portland 251'
>>> for spkr in event.speakers: # <6>
... print('{0.serial}: {0.name}'.format(spkr))
...
speaker.3471: Anna Martelli Ravenscroft
speaker.5199: Alex Martelli
# END SCHEDULE2_DEMO
>>> db.close()
"""
# BEGIN SCHEDULE2_RECORD
import warnings
import inspect # <1>
import osconfeed
import shelve
DB_NAME = 'data/schedule2_db' # <2>
CONFERENCE = 'conference.115'
class Record:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def __eq__(self, other): # <3>
if isinstance(other, Record):
return self.__dict__ == other.__dict__
else:
return NotImplemented
# END SCHEDULE2_RECORD
# BEGIN SCHEDULE2_DBRECORD
class MissingDatabaseError(RuntimeError):
"""Raised when a database is required but was not set.""" # <1>
class DbRecord(Record): # <2>
__db = None # <3>
@staticmethod # <4>
def set_db(db):
DbRecord.__db = db # <5>
@staticmethod # <6>
def get_db():
return DbRecord.__db
@classmethod # <7>
def fetch(cls, ident):
db = cls.get_db()
try:
return db[ident] # <8>
except TypeError:
if db is None: # <9>
msg = "database not set; call '{}.set_db(my_db)'"
raise MissingDatabaseError(msg.format(cls.__name__))
else: # <10>
raise
def __repr__(self):
# 如果DbRecord有serial属性,则返回对应serial对象的value值
if hasattr(self, 'serial'): # <11>
# cls_name = 'DbRecord'
cls_name = self.__class__.__name__
return '<{} serial={!r}>'.format(cls_name, self.serial)
else:
return super().__repr__() # <12>
# END SCHEDULE2_DBRECORD
# BEGIN SCHEDULE2_EVENT
class Event(DbRecord): # <1>
@property
def venue(self):
# 注意venue_serial是event数据中的一个key,里面记录了venue的serial id
key = 'venue.{}'.format(self.venue_serial)
# self.__class__获得是Event类,然后通过类调用类方法fetch(继承自DbRecord)
# 为何不直接用self.fetch(key),因为如果Event中如果有一个属性名为fetch,那么self.fetch就直接获取`fetch`属性对应的值了
# 而不是调用类的fetch方法: 返回db[key]
return self.__class__.fetch(key) # <2>
@property
def speakers(self):
if not hasattr(self, '_speaker_objs'): # <3>
# event中有一个speaker属性,里面记录了该event对应的speakers id列表
spkr_serials = self.__dict__['speakers'] # <4>
fetch = self.__class__.fetch # <5>
# 返回db[speaker.xx]对应的值,并存放在_speaker_objs属性中
self._speaker_objs = [fetch('speaker.{}'.format(key))
for key in spkr_serials] # <6>
return self._speaker_objs # <7>
def __repr__(self):
# 如果记录中name属性,则使用如何格式化方式展示数据
if hasattr(self, 'name'): # <8>
cls_name = self.__class__.__name__
return '<{} {!r}>'.format(cls_name, self.name)
else:
return super().__repr__() # <9>
# END SCHEDULE2_EVENT
# BEGIN SCHEDULE2_LOAD
def load_db(db):
raw_data = osconfeed.load()
warnings.warn('loading ' + DB_NAME)
for collection, rec_list in raw_data['schedule'].items():
record_type = collection[:-1] # <1>
# 比如event变成Event,speakers变成Speakers
cls_name = record_type.capitalize() # <2>
# 从模块的全局作用域中获取cls_name名字对应的对象(也有可能是类,比如Event);如果找不到对象,使用DbRecord
cls = globals().get(cls_name, DbRecord) # <3>
# 如果获取的对象是类 并且是DbRecord类的子类
if inspect.isclass(cls) and issubclass(cls, DbRecord): # <4>
factory = cls # <5>
else:
factory = DbRecord # <6>
# rec_list的Sample data:
"""
"events":[
{"serial":4505,
"name": "Go to the island",
"event_type":"escape"
},
{"serial":4506,
"name": "To be my best",
"event_type":"study"
}],
"""
for record in rec_list: # <7>
# record的Sample data:
"""
{"serial":4505,
"name": "Go to the island",
"event_type":"escape"
},
"""
# Key = Event.4505
key = '{}.{}'.format(record_type, record['serial'])
# record的serial属性值被替换
"""
{"serial":Event.4505,
"name": "Go to the island",
"event_type":"escape"
},
"""
record['serial'] = key
# 将新的record放到db中,具体的数据类型取决于 factory,而factory又取决于全局作用域中定义的类(本例中定义了Event类)
## 1.首先raw_data['Schedule']中有四个Key,其经过转换后(掐尾,以及首字母大写)分别变成:Conference,Event,Speaker,Venus
## 2.由于本文件中只定义了上面4个key中的一个类Event,因此只有对应的Event数据以Event类型数据存放,其它都是以DbRecord
## 类型存放
db[key] = factory(**record) # <8>
# END SCHEDULE2_LOAD
if __name__ == '__main__':
#1. factory(**record)数据类型的验证
cls1 = globals().get('Event', DbRecord)
print(cls1) #
## Speaker, Conference 和 Venus 类都未定义,所以factory都指向DbRecord
cls2 = globals().get('Speaker', DbRecord)
print(cls2) #
cls3 = globals().get('Conference', DbRecord)
print(cls3) #
cls4 = globals().get('Venus', DbRecord)
print(cls4) #
# 2.创建db:会在指定目录自动创建
db = shelve.open(DB_NAME)
# 3.将osconfeed.json数据加载到db中
if CONFERENCE not in db:
load_db(db)
print(db)
for key,value in db.items():
print('Key:',key,' value:',value)
"""打印结果如下:
# 说明conference.115对应的value为何是,首先是Event类中__repr__方法的定义,由于conferenc.115没有名字为name的属性
因此会去调用DbRecord中的__repr__方法,而其返回的是:'<{} serial={!r}>'.format(cls_name, self.serial)
Key: conference.115 value:
# event.4505对应的value,由于Event类中__repr__方法的定义,由于event.4505有名字为name的属性
因此直接返回: return '<{} {!r}>'.format(cls_name, self.name)
Key: event.4505 value:
Key: event.4506 value:
# 同conference
Key: speaker.8890 value:
Key: speaker.8891 value:
Key: venu.1462 value:
"""
# 4.将db赋值给 DbRecord
DbRecord.set_db(db)
# 5.获取db中key = event.4505对应的value
event = DbRecord.fetch('event.4505')
# 由于Event类中定义了__repr__方法,打印event是会调用该方法
# return '<{} {!r}>'.format(cls_name, self.name)
# 其中cls_name是类名,self.name是name关键字对应的Value
print(event) #
## 观察该event中的属性
print(event.__dict__)# {'serial': 'event.4505', 'name': 'Go to the island', 'event_type': 'escape', 'venus_serial': 1462, 'speakers': [8890, 8891]}
# 6.打印该event的中venue:由于给Event类中venue方法添加了property属性,所以能够像属性一样访问该方法
print(event.venue) #
print(event.venue.name) # F151
#7.打印speakers
for spkr in event.speakers:
"""
speaker.8890:Jacky
speaker.8891:Tom
"""
print('{0.serial}:{0.name}'.format(spkr))
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/3/16 8:27
# @Author : Maple
# @File : 04-使用特性验证属性(1).py
# @Software: PyCharm
class LineItem:
"""需要对类的属性weight做一些限制(使用装饰器的方式实现-比较新的一种方式)
>> 其值必须大0
>> 可以通过特性的方式实现
"""
def __init__(self,description,weight,price):
self.description = description
self.weight = weight
self.price = price
@property
def weight(self):
# weight属性的值真正存放于__weight中
return self.__weight
@weight.setter
def weight(self,value):
# weight的值必须大于0
if value > 0:
self.__weight = value
else:
raise ValueError('weight must be > 0')
def subtotal(self):
return self.weight * self.price
if __name__ == '__main__':
# 1.观察类属性:注意LineItem有一个类属性,并且是一个特性property
print(LineItem.__dict__) # 'weight':
# 2.查看LineItem类的属性
item = LineItem('Bread',20,100)
## 注意观察,item的属性有一个是_LineItem__weight,表明weight属性值真正存放于__weight属性(实例属性)中
print(item.__dict__) # {'description': 'Bread', '_LineItem__weight': 20, 'price': 100}
# 3. 特性验证:weight是否能设置小于0的值
# 以下代码会报错
item2 = LineItem('Bread', -10, 100) # ValueError: weight must be > 0
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/3/16 8:38
# @Author : Maple
# @File : 05-使用特性验证属性(2).py
# @Software: PyCharm
class LineItem:
"""需要对类的属性weight做一些限制(使用经典方式实现-手动配置和装载property属性)
>> 其值必须大0
>> 可以通过特性的方式实现
"""
def __init__(self,description,weight,price):
self.description = description
self.weight = weight
self.price = price
def subtotal(self):
return self.weight * self.price
# 方法名以get开头只是一种约定俗成
def get_weight(self):
return self.__weight
# 方法名以set开头只是一种约定俗成
def set_weitht(self,value):
if value > 0:
self.__weight = value
else:
raise ValueError('weight must be > 0')
# 手动为weight添加特性
# property 构造方法的完整签名: property(gfet_None,fset=None,fdel=None,doc= None)
# 本案例构造了一个特性weight(注意与属性weight同名,此时就涉及到特性与属性优先级的问题,留待下节分解),且为该特性添加了
# get_weight和set_weitht方法,这样当通过obj.weight方式访问属性或者赋值时,就会分别走这两个函数的逻辑
weight = property(get_weight,set_weitht)
if __name__ == '__main__':
# 1. 特性验证:weight是否能设置小于0的值
# 以下代码会报错
try:
item1 = LineItem('Bread', -10, 100) # ValueError: weight must be > 0
except ValueError as e:
print(e)
# 2.特性验证: 是否能够将weight的值修改为复数
item2 = LineItem('Milk', 10, 200)
print(item2.weight) # 10
# 2-1 修改weight的值为另外一个大于0的值
item2.weight = 20
print(item2.weight) # 20
# 2-2 试图修改weight的值为复数,会报错
try:
item2.weight = -20 # ValueError: weight must be > 0
except:
pass
# 3.观察类属性:注意LineItem有一个类属性,并且是一个特性property
print(LineItem.__dict__) # weight':
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/3/16 8:50
# @Author : Maple
# @File : 06-类属性,实例属性和特性的优先级.py
# @Software: PyCharm
"""
1. 实例属性会遮盖类属性
2. 实例属性不会遮盖类特性
"""
class Person:
# 类属性
data = "I am a Person"
# 特性
@property
def prop(self):
return 'the prop value'
if __name__ == '__main__':
# 1. 实例属性会遮盖类属性
print('*****1. 实例属性会遮盖类属性************')
p = Person()
## 1-1 返回实例属性:为空,因为未定义实例属性
print(vars(p))
## 1-2 访问类属性
print(p.data) # I am a Person
## 1-3 添加实例属性
p.data = 'Maple'
# 此时实例会有一个data实例属性
print(vars(p)) # {'data': 'Maple'}
## 1-4 再次通过实例访问data属性
# 此时访问到的是实例属性,而不是类属性
print(p.data) # Maple
## 1-5 类属性data的值并未发生
print(Person.data) # I am a Person
# 2. 实例属性不会遮盖类特性
print('*****2. 实例属性不会遮盖类特性*************')
## 2-1 通过类访问类特性prop,返回特性对象
print(Person.prop) #
## 2-2 通过实例对象访问特性prop,返回 return的值
print(p.prop) # the prop value
## 2-3 尝试直接设置prop实例属性,会失败
try:
p.prop = 'foo'
except Exception as e:
print(e) # can't set attribute
## 2-4 直接通过self.__dict__方法添加属性
p.__dict__['prop'] = 'foo'
# 此时实例对象p中会新增prop实例属性
print(vars(p)) #{'data': 'Maple', 'prop': 'foo'}
## 2-5 但是此时通过实例访问prop,仍然访问的特性(而非实例属性)
print(p.prop) # the prop value
## 2-6 覆盖prop特性
## 此时pro还是特性
print(Person.__dict__) ## {...'prop': ,...}
## 覆盖prop特性,此时prop变成类属性
Person.prop = 'bar'
print(Person.__dict__)# {...,'prop': 'bar'...}
## 2-7 由于实例属性会遮盖类属性
# 此时访问到的就是 实例属性
print(p.prop) # foo
# 3 为类新增一个data`特性`,此时特性会遮盖`实例属性`
Person.data =property(lambda self: "I am Person prop data")
# 3-1 此时再访问data就是,访问读取特性的值
print(p.data) # I am Person prop data
## 3-2 删除特性,再次访问就是访问实例属性
del Person.data
print(p.data) # Maple
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/3/16 9:22
# @Author : Maple
# @File : 07-定义特性工厂.py
# @Software: PyCharm
def quantity(storage_name):
def qty_getter(instance):
return instance.__dict__[storage_name]
def qty_setter(instance,value):
if value > 0:
instance.__dict__[storage_name] = value
else:
raise ValueError('Value must be > 0')
return property(qty_getter,qty_setter)
class LineItem:
# 以下是两个特性
weight = quantity('weight')
price = quantity('price')
def __init__(self,description,weight,price):
self.description = description
# 注意以下两句已经不是单纯地给LineItem类属性赋值
# 1. 因为LineItem类定义了两个类属性: weight = quantity('weight')和 price = quantity('price'),并且其为特性
# 而类特的优先级比实例属性更高
# 2. 因此当self调用weight赋值的时,会调用qty_setter方法,并且在该方法中完成实例属性赋值:
# instance.__dict__[storage_name] = value【可观察到:实例属性的名字由 storage_name 决定,而storage_name来源于
# quantity()中传递进来的参数名,由此也应该注意:实例属性的名其实并非固定的】
self.weight = weight
self.price = price
def subtotal(self):
return self.weight * self.price
if __name__ == '__main__':
# 1.查看类的属性,可以发现weight和price是两个特性
print(LineItem.__dict__) ## {...'weight': , 'price': ...}
computer = LineItem('Computer',20,10000)
# 2. 实例有两个同名的属性:weight和price
print(computer.__dict__) # {'description': 'Computer', 'weight': 20, 'price': 10000}
# 3.特性的工作流程
## (1) 通过实例访问weight,由于特性优先级大于实例属性,所以实际上会走特性的qty_getter方法
## (2) 所以返回computer.__dict['weight'],即实例属性中存放的值
print(computer.weight) # 20
## (1) 通过实例修改weight,由于特性优先级大于实例属性,所以实际上会走特性的qty_setter方法
## (2) 先判断新值是否大于0,如果满足条件,则执行:computer.__dict['weight'] = value,即给实例属性赋予新值
computer.weight = 30
print(computer.weight) # 30
## (1) 通过实例修改weight,由于特性优先级大于实例属性,所以实际上会走特性的qty_setter方法
## (2) 先判断新值是否小于0,如果小于0,则会抛出ValueError
computer.weight = -30 # ValueError: Value must be > 0
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2024/3/16 10:12
# @Author : Maple
# @File : 08-删除属性.py
# @Software: PyCharm
"""
不常用,仅作展示
"""
class BlackKnight:
def __init__(self):
self.members = ['am arm','another arm','a leg','another leg']
self.phrase = ["'Tis but a scrath'","It's just a fresh wound"
,"I'm invincible!","All right,we'll call it a draw:"]
@property
def member(self):
print('next member is:')
return self.members[0]
@member.deleter
def member(self):
text = 'BLACK KNIGHT (loses {}) \n -- {}'
print(text.format(self.members.pop(0),self.phrase.pop(0)))
if __name__ == '__main__':
knight = BlackKnight()
# next member is:
# am arm
print(knight.member)
# BLACK KNIGHT (loses am arm)
# -- 'Tis but a scrath'
del knight.member
# next member is:
# another arm
print(knight.member)