Extending
hasattr(self, ‘xxx’)
課外補充:
class attribute vs. instance attribute
Delegating to Parent
super().method()
will call method in the parent, but bound to the instance it is called from.
提醒:If you forget super() in super().sing() and use self.sing() you’ll end up with infinite recursion!
https://www.796t.com/post/N2w5d3E=.html
Delegation and Method Binding
I’m basically going to create attributes or properties for area and perimeter and I’m going to use lazy evaluation and caching.
UnitCircle 避免被設定新的半徑值
Slots
Only use slots in cases where you know that you will benefit both memory overhead and speed.
缺點:
- we cannot add attributes to our objects(instance) that are not defined in slots.
- cause difficulties in multiple inheritance.
__dict__
和 __slot__
所使用的記憶體對照表
__init__(self, x, y) |
__slot__(x, y) |
|
---|---|---|
10,000 intances | 1,729KB | 635KB |
Slots and Single Inheritance
Parent slots | Children slots | |
---|---|---|
1 | ||
2 | ||
3 | ||
4 | Slots and Properties | |
5 | slots & __dict__ 並存 |
1. Parent 有 slots, Children 沒有
這種情形下,除了 Parent 的 slots 中指定的變數(本例為 name)外,Children 保有自己的
__dict__
。
class Person:
__slots__ = 'name',
def __init__(self, name):
self.name = name
class Student(Person):
pass
p = Person('Eric')
try:
print(p.__dict__)
except AttributeError as ex:
print(ex)
輸出:
Person
object has no attribute__dict__
s = Student('Alex')
s.name, s.__dict__
輸出:(‘Alex’, {})
name 儲存在 Slot,但仍然保有
__dict__
(但目前還沒有值)
s.age = 19
s.__dict__
s.name, s.age
輸出:{‘age’: 19} 這是
__dict__
的值輸出:(‘Alex’, 19) name(Alex) 儲存在 slot、age(19) 儲存在
__dict__
2. Parent 和 Children 都有 slots
這種情形下,Children 和 Parents 一樣,也沒有
__dict__
。所以變數只有從 Parent Slots 繼承過來的(本例為),和自己 slots 的(本例為)。兩個提醒:
不同的 slots:Children 的 slots,避免與 Parent 重覆。
tuple():Children 的 slots 不一定要明確定義,即使只是空白的
tuple()
也可以。
class Person:
__slots__ = 'name',
def __init__(self, name):
self.name = name
class Student(Person):
__slots__ = 'school', 'student_number'
def __init__(self, name, school, student_number):
super().__init__(name)
self.school = school
self.student_number = student_number
s = Student('James', 'MI6 Prep', '007')
s.name, s.school, s.student_number
輸出:(‘James’, ‘MI6 Prep’, ‘007’)
全部儲存在 slots,name 繼承自 Parent,school和 student_number 則來自 Child 自己的 slots。
3. Parent 無 slot, Children 有 slots
和第一種情形類似,Children 既有 slots 中指定的變數(本例為 name),同時仍有
__dict__
。問題:Children 的
__dict__
,是否只能儲存 Parent 的變數?答案是可以儲存任何新增的變數,就像一般的__dict__
行為一樣。
class Person:
def __init__(self, name):
self.name = name
class Student(Person):
__slots__ = 'age',
def __init__(self, name, age):
super().__init__(name)
self.age = age
s = Student('Python', 30)
s.name, s.age, s.__dict__
s.grade = 9
s.grade, s.name, s.age, s.__dict__
輸出:(‘Python’, 30, {‘name’: ‘Python’})
name(Python) 儲存在
__dict__
,age(30) 則在 slot。輸出:(9, ‘Python’, 30, {‘name’: ‘Python’, ‘grade’: 9})
4. Relation between Slots and Properties - descriptor
Data descriptors are simply classes that implement certain methods, just like iterators
iterators:
__iter__
,__next__
descriptors :__get__
,__set__
等
class Person:
__slots__ = '_name', 'age'
def __init__(self, name, age):
self.name = name
self.age = age
@property
def name(self):
return self._name
@name.setter
def name(self, name):
self._name = name
輸出:
5. Slots 和 instance dictionary(__dict__
) 並存
想要同時擁有 slot 的效能,和
__dict__
新增屬性的彈性,方法很簡單,把__dict__
加入__slots__
即可。PS:不使用繼承的方法
class Person:
__slots__ = 'name', '__dict__'
def __init__(self, name, age):
self.name = name
self.age = age
p = Person('Alex', 19)
p.name, p.age, p.__dict__
輸出:(‘Alex’, 19, {‘age’: 19})
p.school = 'Berkeley'
p.__dict__
輸出:{‘age’: 19, ‘school’: ‘Berkeley’}
補充
老師的獨特幽默:巨蟒劇團 (Monty Python) 的一首伐木工經典歌曲
QA
Q: 講座 74 中您建議返回 NotImplemented
一個“抽象”基類方法。
提高不是更合適NotImplementedError
嗎?請參閱 NotImplementedError。
A: 這取決於上下文。通常 NotImplemented
返回用於實現二元運算符(例如等於、小於等於)的方法。這樣 Python 將嘗試執行反射操作。
另一方面,如果你正在創建某種你想定義“抽象”方法的基類,那麼你應該引發異常NotImplementedError
。
在這些練習中,我們正在實現二元運算符,因此返回 NotImplemented
似乎更合適。相等運算符可能有點不同——如果兩個對像根本不可比較(例如,它們有不同的模組),那麼我可能不關心正在嘗試的反射,在這種情況下,立即提高可能是好的,而NotImplementedError
不是讓 Python 嘗試執行反映相等的操作,只是想出相同的返回值 NotImplemented
- 這樣我就節省了一個操作。
class Account:
apr = 3.0
def __init__(self, account_number, balance):
self.account_number = account_number
self.balance = balance
self.account_type = 'Generic Account'
def calc_interest(self):
return f'Calc interest on {self.account_type} with APR = {type(self).apr}'
# return f'Calc interest on {self.account_type} with APR = {self.__class__.apr}'
class Savings(Account):
apr = 5.0
def __init__(self, account_number, balance):
self.account_number = account_number # We'll revisit this later - this is clumsy
self.balance = balance
self.account_type = 'Savings Account'