python學習-基礎-面向物件程式設計(高階)
阿新 • • 發佈:2019-01-14
面向物件程式設計高階
# __slots__ # Python允許在定義class的時候,定義一個特殊的__slots__變數,來限制該class例項能新增的屬性: # __slots__定義的屬性僅對當前類例項起作用,對繼承的子類是不起作用的 # 除非在子類中也定義__slots__,這樣,子類例項允許定義的屬性就是自身的__slots__加上父類的__slots__。 class Student3(object): __slots__ = ('name', 'age') # 用tuple定義允許繫結的屬性名稱 s1 = Student3() # 建立新的例項 s1.name = 'Michael' # 繫結屬性'name' s1.age = 25 # 繫結屬性'age' #s1.score = 99 # 繫結屬性'score' 但是會報錯 AttributeError: 'Student' object has no attribute 'score'。
# @property # Python內建的@property裝飾器就是負責把一個方法變成屬性呼叫的 # 把一個getter方法變成屬性,只需要加上@property就可以了, # 此時,@property本身又建立了另一個裝飾器@score.setter,負責把一個setter方法變成屬性賦值 class Student4(object): @property def score(self): return self._score @score.setter def score(self, value): if not isinstance(value, int): raise ValueError('score must be an integer!') if value < 0 or value > 100: raise ValueError('score must between 0 ~ 100!') self._score = value s4 = Student4() s4.score = 60 print(s4.score) # 60 # 利用@property給一個Screen物件加上width和height屬性,以及一個只讀屬性resolution: class Screen(object): """docstring for Screen""" def __init__(self): super(Screen, self).__init__() # self.arg = arg self.resolution = 786432 @property def width(self): return self._width @property def height(self): return self._height @property def resolution(self): return self._height * self._width @width.setter def width(self, val): self._isValidNumber(val) self._width = val @height.setter def height(self, val): self._isValidNumber(val) self._width = val # 檢查錯誤 def _isValidNumber(self, val): if not isinstance(val, (int, float)): raise ValueError('type error') elif val < 0: raise ValueError('must positive number')
# 通過多重繼承,一個子類就可以同時獲得多個父類的所有功能。 class Animal1(object): pass # 大類: class Mammal(Animal): pass class Bird(Animal): pass # 功能類 class Runnable(object): def run(self): print('Running...') class Flyable(object): def fly(self): print('Flying...') # 各種動物: 多繼承 class Dog(Mammal, Runnable): pass class Bat(Mammal, Flyable): pass class Parrot(Bird, Runnable): pass class Ostrich(Bird, Flyable): pass
#MixIn的目的就是給一個類增加多個功能,這樣,在設計類的時候,
#我們優先考慮通過多重繼承來組合多個MixIn的功能,而不是設計多層次的複雜的繼承關係。
# class Dog3(Mammal, RunnableMixIn, CarnivorousMixIn):
# pass
# 編寫一個多程序模式的TCP服務
# class MyTCPServer(TCPServer, ForkingMixIn):
# pass
# 多執行緒模式的UDP服務
# class MyUDPServer(UDPServer, ThreadingMixIn):
# pass
#
# __str__ 返回一個可以讀懂字串 Student object (name: Michael)
# __repr__() 返回程式開發者看到的字串 <__main__.Student object at 0x109afb310>
# 通常__str__()和__repr__()程式碼都是一樣的,所以,有個偷懶的寫法:
class Student5(object):
"""docstring for Student5"""
def __init__(self, name):
# super(Student5, self).__init__()
self._name = name
def __str__ (self):
return 'Student object (name: %s)' % self._name
__repr__ = __str__
print(Student5('Jack'))
# __iter__
# 如果一個類想被用於for ... in迴圈,類似list或tuple那樣,就必須實現一個__iter__()方法,該方法返回一個迭代物件,
# 然後,Python的for迴圈就會不斷呼叫該迭代物件的__next__()方法拿到迴圈的下一個值,直到遇到StopIteration錯誤時退出迴圈。
# __getitem__ 取下標
# __getattr__ 屬性或方法不存在,取設定的預設值
class Fib1(object):
def __init__(self):
self.a, self.b = 0, 1 # 初始化兩個計數器a,b
def __iter__(self):
return self # 例項本身就是迭代物件,故返回自己
def __getitem__(self, n): # 像陣列一樣可以用下標取值, 可以使用切片
if isinstance(n, int): # n是索引
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
if isinstance(n, slice): # n是切片
start = n.start
stop = n.stop
if start is None:
start = 0
a, b = 1, 1
L = []
for x in range(stop):
if x >= start:
L.append(a)
a, b = b, a + b
return L
def __getattr__(self, attr):
if attr=='score':
return 99
def __next__(self):
self.a, self.b = self.b, self.a + self.b # 計算下一個值
if self.a > 100000: # 退出迴圈的條件
raise StopIteration()
return self.a # 返回下一個值
# REST API 可以利用完全動態的__getattr__,我們可以寫出一個鏈式呼叫:
class Chain(object):
def __init__(self, path=''):
self._path = path
def __getattr__(self, path):
return Chain('%s/%s' % (self._path, path))
def __str__(self):
return self._path
__repr__ = __str__
print(Chain().status.user.timeline.list)
# __call__
# 一個物件例項可以有自己的屬性和方法,當我們呼叫例項方法時,
# 我們用instance.method()來呼叫。能不能直接在例項本身上呼叫呢?在Python中,答案是肯定的。
# __call__()還可以定義引數
# 任何類,只需要定義一個__call__()方法,就可以直接對例項進行呼叫
class Student6(object):
def __init__(self, name):
self.name = name
def __call__(self):
print('My name is %s.' % self.name)
s6 = Student6('Mick')
print(s6())
# 我們需要判斷一個物件是否能被呼叫,能被呼叫的物件就是一個Callable物件
# 通過callable()函式,我們就可以判斷一個物件是否是“可呼叫”物件。
print(callable(Student3()))
from enum import Enum, unique
DayT= Enum('one', ('A', 'b', 'C','D'))
# 通過列舉獲取所有的成員
for t, member in DayT.__members__.items():
print(t, '=>', member, ',', member.value)
# value屬性則是自動賦給成員的int常量,預設從1開始計數。
# 如果需要更精確地控制列舉型別,可以從Enum派生出自定義類unique
# @unique裝飾器可以幫助我們檢查保證沒有重複值。
@unique
class WeekDay (Enum):
Sun = 0 # Sun的value被設定為0
Mon = 1
Tue = 3
Wed = 2
for t, member in WeekDay.__members__.items():
print(t, '=>', member, ',', member.value)
# Weekday.Mon Weekday['Tue'] Weekday(1)
#
# 把Stu1的gender屬性改造為列舉型別,可以避免使用字串:
@unique
class Gender (Enum):
"""docstring for Gender"""
Male = 0
Female = 1
class Stu2(object):
"""docstring for Stu1"""
def __init__(self, name, gender):
# super(Stu2, self).__init__()
self.name = name
self.gender = gender
bart = Stu2('Bart', Gender.Male)
print(bart.gender == Gender.Male)
# type() ,type函式可以檢視一個型別或變數的型別,也可以建立新的型別
# 要建立一個class物件,type()函式依次傳入3個引數:
#class的名稱;
#繼承的父類集合,注意Python支援多重繼承,如果只有一個父類,別忘了tuple的單元素寫法;
#class的方法名稱與函式繫結,這裡我們把函式fn繫結到方法名hello上。
# Hello = type('Hello', (object,), dict(hello=fn)) # 建立Hello class
#
# metaclass
# 除了使用type()動態建立類以外,要控制類的建立行為,還可以使用metaclass。metaclass,直譯為元類,基本不會用到