1. 程式人生 > >python學習-基礎-面向物件程式設計(高階)

python學習-基礎-面向物件程式設計(高階)

面向物件程式設計高階

  1. slots

# __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'。

  1. @property

# @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')
  1. 多重繼承

# 通過多重繼承,一個子類就可以同時獲得多個父類的所有功能。
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

  1. MixIn

#MixIn的目的就是給一個類增加多個功能,這樣,在設計類的時候,
#我們優先考慮通過多重繼承來組合多個MixIn的功能,而不是設計多層次的複雜的繼承關係。
# class Dog3(Mammal, RunnableMixIn, CarnivorousMixIn):
#     pass
#     編寫一個多程序模式的TCP服務
# class MyTCPServer(TCPServer, ForkingMixIn):
#     pass
#     多執行緒模式的UDP服務
# class MyUDPServer(UDPServer, ThreadingMixIn):
#     pass
#     
  1. 定製類

    • __str__
    • __repr__
    • __iter__
    • __getitem__
    • __getattr__( REST API 可以利用完全動態的__getattr__)
    • __call__
# __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()))
  1. 列舉類

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)

  1. 元類

# type() ,type函式可以檢視一個型別或變數的型別,也可以建立新的型別
# 要建立一個class物件,type()函式依次傳入3個引數:

#class的名稱;
#繼承的父類集合,注意Python支援多重繼承,如果只有一個父類,別忘了tuple的單元素寫法;
#class的方法名稱與函式繫結,這裡我們把函式fn繫結到方法名hello上。
# Hello = type('Hello', (object,), dict(hello=fn)) # 建立Hello class
# 
# metaclass
# 除了使用type()動態建立類以外,要控制類的建立行為,還可以使用metaclass。metaclass,直譯為元類,基本不會用到