1. 程式人生 > >python入門系列:面向對象

python入門系列:面向對象

位置 () class a 動態類型 相關 簡化 單純 self 未定義

類和對象的創建

經典類 沒有繼承 object的類

新式類 繼承了 object的類

class Money: # 2.x中默認是經典類,3.x中是新式類
pass

class Money(object): # 兼容的一種寫法
pass

Money既是類的name屬性名,又是一個引用該類的變量

print(Money.name) # Money
xxx = Money
print(xxx.name) # Money
對象

one = Money()
print(one) # <main.Money object at 0x000001555E9534A8>
print(one.class) # <class ‘main

.Money‘>
屬性相關

對象屬性

class Person:
pass
p = Person()

給 p對象增加屬性, 所有的屬性是以字典的形式組織的

p.age = 18
print(p.age) # 18
print(p.dict) # {‘age‘: 18}
print(p.sex) # AttributeError: ‘Person‘ object has no attribute ‘sex‘

刪除p對象的屬性

del p.age
print(p.age) # AttributeError: ‘Person‘ object has no attribute ‘age‘
類屬性

class Money:

num = 666
count = 1
type = "rmb"

print(Money.num) # 666

對象查找屬性,先到對象自身去找,若未找到,根據 class找到對應的類,然後去類中查找

one = Money()
print(one.count) # 1

不能通過對象去 修改/刪除 對應類的屬性

one.num = 555 # 實際上是給 one 對象增加了一個屬性
print(Money.num) # 666
print(one.num) # 555

類屬性會被各個對象共享

two = Money()
print(one.num, two.num) # 666 666
Money.num = 555

print(one.num, two.num) # 555 555
限制對象的屬性添加

類中的 slots屬性定義了對象可以添加的所有屬性

class Person:
slots = ["age"] # 只允許添加一個 age屬性

p1 = Person()
p1.age = 1
p1.num = 2 # AttributeError: ‘Person‘ object has no attribute ‘num‘
私有化屬性

Python沒有真正的私有化支持,只能用給變量添加下劃線來實現偽私有;通過名字重整機制
屬性的訪問範圍:類的內部-->子類內部-->模塊內的其他位置-->其他模塊
公有屬性 x 的訪問範圍

類的內部
子類內部
模塊內的其他位置
子類內部
受保護屬性 _x 的訪問範圍

類的內部
子類內部
模塊內的其他位置(但不推薦)
子類內部(from ... import xxx 不可以訪問,要指明all變量)
私有屬性 __x 的訪問範圍

類的內部
子類內部
模塊內的其他位置
子類內部(同_x)
保護數據案例

class Person:
def init(self):
self.__age = 18

def set_age(self, age): # 錯誤數據的過濾
if isinstance(age, int) and 0 < age < 150:
self.__age = age
else:
print("Wrong age value")

def get_age():
return self.__age

p = Person()
print(p.get_age()) # 18
p.set_age(22)
print(p.get_age()) # 22
只讀屬性

1. 屬性私有化 + 屬性化 get()方法

class Person(object):
def init(self):
self.__age = 18

可以以使用屬性的方式來使用方法

@property
def age(self):
return self.__age
p = Person()
print(p.age) # 18
p.age = 666 # Attribute Error: can‘t set attribute

2. 通過底層的一些函數

class Person:

通過 屬性 = 值 的方式來給一個對象增加屬性時,底層都會調用這個方法,構成鍵值對,存儲在 dict字典中

可以考慮重寫底層的這個函數,達到只讀屬性的目的

def setattr(self, key, value):
if key == "age" and key in dict:
print("read only attribute")
else:
self.dict[key] = value
方法相關

方法的劃分

實例方法
類方法
靜態方法
class Person:
def instance_fun(self): # self: 調用對象的本身,調用時不用寫,解釋器會傳參
print("instance method", self)

@classmethod
def class_fun(cls): # cls: 類本身
print("class method", cls)

@staticmethod
def static_fun():
print("static method")
所有的方法都存儲在類中,實例中不存儲方法
類方法和靜態方法無法訪問實例屬性
方法的私有化

和變量的私有化思想差不多
class Person:
__age = 18

def __run(self): # 只能在該類中被調用
print("running...")
元類

創建類對象的類(類也是一個對象)
a, s = 8, "123"
print(a.class, s.class) # <class ‘int‘> <class ‘str‘>
print(int.class, str.class) # <class ‘type‘> <class ‘type‘>
type是元類。
技術分享圖片

通過type元類來創建類,動態創建。也可以用metaclass來指明元類,進行類的創建。
檢測類對象中是否有 metaclass屬性
檢測父類中是否有 metaclass屬性
檢測模塊中是否有 metaclass屬性
通過內置的type來創建類
def run(self):
print("run...")

Dog = type("Dog", (), {"count": 0, "run": run})
print(Dog) #
d = Dog()
print(d.count) # 0
print(d.run()) # run...
更加詳細的內容,在進高級部分的元類編程講解

內置的特殊屬性
技術分享圖片
內置的特殊方法(魔法函數)

這裏只做一個了解,高級部分會詳細地講解魔法函數。可以理解為在類中實現了這些特殊的函數,類產生的對象可以具有神奇的語法效果。

信息格式化操作

calss Person:
def init(self, n, a):
self.name = n
self.age = a

面向用戶

def str(self):
return "name: %s, age: %d" % (self.name, self.age)

面向開發人員

def repr(self):

todo

一般默認給出對象的類型及地址信息等

打印或進行格式轉換時,先調用 str()函數,若未實現,再調用 repr()函數

p = Person("Rity", 18)
print(p) # name: Rity, age: 18
res = str(p)
print(res) # name: Rity, age: 18
print(repr(p)) # <main.Person object at 0x000001A869BEB470>
調用操作

使得一個對象可以像函數一樣被調用

class PenFactory:
def init(self, type):
self.type = type

def call(self, color):
print("get a new %s, its color is %s" % (self.type, color))

pencil = PenFactory("pencil")
pen = PenFactory("pen")

一下兩種使用方式會調用 call()函數

pencil("red") # get a new pencil, ites color is red
pencil("blue") # get a new pencil, ites color is blue
pen("black") # get a new pen, ites color is black
索引操作

class Person:
def init(self):
self.cache = {}

def setitem(self, key, value):
self.cache[key] = value

def getitem(self, key):
return self.cache[key]

def delitem(self, key):
del self.cache[key]

p = Person()
p["name"] = "MetaTian"
...
比較操作

使得自己定義的類可以按照一定的規則進行比較

import [email protected]_ordering
br/>@functools.total_ordering
def init(self, age, height):
self.age = age
self.height = height

def eq(self, other): # ==
return self.age == other.age

def lt(self, ohter): # <
return self.age < other.age

a1, a2 = A(18, 170), A(19, 178)

因為邏輯具有相反性,當使用 > 時,首先會查找 gt()函數,若未定義,將參數交換後調用 lt()方法

由 == 和 < 可以組合出其他的所有比價邏輯,使用裝飾器可以自動生成其他邏輯對應的函數,簡化代碼

print(a1 < a2) # True
print(a2 > a1) # True
print(a1 >= a2) # False
描述器

描述器是一個對象,用來描述其他對象屬性的操作;作用是對屬性的操作做驗證和過濾。
前面只讀屬性案例中就是用到了描述器。
在對象的內部增加一個描述器,可以接管對象屬性的增刪改查操作。
class Age:
def get(self, instance, owner): # instance是擁有 age 屬性的對象
pass

def set(self, instance, value):
instance.v = value # 將變量的值綁定在 Person 的實例中

def delete(self, instance):
pass

class Person:
age = Age()

age實例是 p1和 p2兩個對象所共享的,所以 Age 對象及實例不應該具有屬性,只單純地提供方法即可

p1 = Person()
p1.age = 19 # 調用 set()
print(p1.age) # 調用 get()
p2 = Person()
p2.age = 20
print(p2.age) # 20
資料描述器和非資料描述器

也可以稱為數據描述器和非數據描述器
資料描述器:實現了get() 和 set()
非資料描述器:僅僅實現了get()
實例屬性和描述器重名時,操作的優先級關系: 資料描述器 > 實例屬性 > 非資料描述器
生命周期

用來表示一個對象從創建到釋放的過程
class Person:
__count = 0

def init(self):
Person.__count += 1

def del(self):
Person.__count -= 1

@classmethod
def log(cls):
print("we have %d people" % cls.__count)

p1 = Person()
Person.log() # we have 1 people
p2 = Person()
Person.log() # we have 2 people
內存管理機制

引言

萬物皆對象,不存在基本數據類型
在 [-5, 正無窮) 範圍內相等的整數和短小的字符串,Python會進行緩存,不會創建多個對象
n1 = 1
n2 = 1
print(id(n1), id(n2)) # 1708655056 1708655056
容器對象:存儲的對象,僅僅是其他對象的引用(列表)
內存回收

引用計數
一個對象會記錄著自身被引用的個數
每增加一個引用,引用數+1,減少一個引用,引用數-1
引用數為0的時候,會被當做垃圾進行回收
會出現兩個對象循環引用的問題
垃圾回收
從經歷過引用計數機制但仍然未被釋放的對象中,進行內存釋放
新增的對象個數 - 消亡對象的個數達到一定閾值時才進行垃圾檢測
分代回收
分代回收是垃圾回收的高效解決方案,不需頻繁地進行垃圾檢測
存活時間越久的對象,越不可能在後面的過程中變成垃圾
設立0, 1, 2三代對象集合,對其中的對象進行不同頻率的檢測
第一次檢測存活下來的,從0代納入1代,0代檢測一定次數後開始檢測1代,以此類推
深拷貝和淺拷貝

淺拷貝

a = [1, 2, 3]
b = a
print(id(a), id(b)) # 2229855665608 2229855665608
深拷貝

import copy
a = [1, 2, 3]
c = copy.deepcopy(a)
print(id(a), id(c)) # 2229855665608 2229861709896
copy和deepcopy的區別

import copy
a = [1, 2, 3]
b = [4, 5, 6]
c = [a, b]
d = copy.deepcopy(c)
e = copy.copy(c)
技術分享圖片
使用copy拷貝可變類型時,進行單層次的深拷貝,若拷貝的是不可變類型,則進行淺拷貝。

面向對象三大特性

封裝

繼承

非私有的屬性和方法可以被繼承,繼承不是拷貝了資源,而是具有了資源的訪問權,資源的存儲位置在父類中,實現資源重用。
Python中可以使用多繼承。

所有的類都繼承了 object 類

所有的類對象都由 type 實例化出來

class A:
pass

class B:
pass

class C(A, B): # C 類繼承了 A和 B類
pass

print(C.bases) # (<class ‘main.A‘>, <class ‘main.B‘>)
print(int.base) # <class ‘object‘>
print(bool.base) # <class ‘int‘>
幾種繼承的形式
技術分享圖片

資源查找順序
單繼承鏈:C-->B-->A
無重疊多繼承鏈:按照單繼承鏈深度優先查找(C-->B-->A-->D-->E)
有重疊多繼承鏈:廣度優先查找(C-->B-->D-->A)
資源覆蓋
在優先級較高的類中重新定義了同名的屬性和方法,再次調用時,會調用到優先級較高類中的資源,並不是相關的資源在內存上被覆蓋了,而是調用優先級出現了變化
self 和 cls

誰調用方法,self 和 cls 就是誰

帶著參數去找方法

class A:
def show(self):
print(self)

@classmethod
def tell(cls):
print(cls)

class B(A):
pass
B.tell() # <class ‘main.B‘>
B().show() # <main.B object at 0x027674D0>
資源的累加
class A:
def init(self):
self.x = 2
class B(A):
pass
class C(A):
def init(self):
self.y = 1
class D(A):
def init(self):
self.y = 1

class E(A):
def init(self):
super().init() # 會調用 A的構造函數,參數可以省略

A.init(self) //和上面等價,要傳參

self.y = 1

b = B()
print(b.x) # 2, 調用了父類的構造函數,b 調用,x就是 b的
c = C()
print(c.y) # 1, C有了構造函數,就調用 C的,A的構造函數不會被調用
print(c.x) # 報錯,沒有這個屬性
e = E()
print(e.x, e.y) # 2, 1
多態

Python是動態類型的語言,不需要嚴格意義上的多態
def test(obj):
obj.func()

只要傳入的參數有 func()這個方法,就可以傳入進行執行,不用進行類型檢測。

不需要按照其他靜態語言那樣沿著繼承鏈進行方法調用形成多態

類的設計原則

單一職責原則:一個類只負責一項職責
開放封閉原則:對外擴展開放,對內修改關閉
裏式替換原則:子類所繼承下來的屬性和方法都需能夠合理地使用
接口分離原則:功能一致的方法應該重新組成新的接口/類,進行細分
依賴倒置原則:高層模塊不應該直接依賴低層模塊,核心是面向接口編程
喜歡python + qun:839383765 可以獲取Python各類免費最新入門學習資料!

python入門系列:面向對象