文章目錄
面向對象
3種常見的編程方式
- 面向過程
- 函數式編程
- 面向對象
面向過程
所謂過程就是我們解決問題的步驟,一步步的按照流程走,有先後之分。
它呢,整個設計就好比流水線,思維上比較機械化。
優缺點:
- 優點
- 複雜的問題流程化,將問題分解簡化。
- 缺點
- 拓展性不好
面向對象
核心是對象。
正式的來說
- 對象是一個數據以及相關行爲的集合
- 面向對象是功能上指向建模對象
通過數據和行爲方式來描述交互對象的集合。
在Python中,一切皆爲對象。
面向對象的優缺點:
- 優點
- 解決程序的拓展性
- 缺點
- 就是複雜度遠高於面向過程
- 交互式解決問題,無法準確預測結果
在現實世界中,以我們爲例
object1:
Tom
特徵:
school=zucc
name=tom
age=21
sex=male
技能:
eat
study
sleep
object2:
Jack
特徵:
school=zucc
name=jack
age=20
sex=male
技能:
eat
study
sleep
sing
類就是類別、種類
對象就是特徵和技能的統一體。
類這是這一系列相似對象的特徵和技能的結合。
對於現實世界,先有個體(即對象),纔有類別;但對於程序,必須先有類,然後纔有對象的。
面向對象編程
OOP(object oriented programming)
就是一種程序設計思想,OOP把對象作爲程序的一個基本單元。一個對象就包含了數據和操作數據的函數。
在Python中,所有數據類型都可以視爲對象,同時,我們也可以自定義對象。
自定義的對象的數據類型就是面向對象中類(class)的概念。
Demo:
假如要處理我們的成績。爲了表示學生的成績:
- 面向過程的方式
stu1 = {"name":"Tom", "score":99}
stu2 = {"name":"Jack", "score":82}
- 利用函數來實現
def find_score(stu):
print(stu["name"], ":", stu["score"])
- 面向對象
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
def find_score(self):
print(self.name, ":", self.score)
面向對象技術簡介
- 類(Class): 用來描述具有相同的屬性和方法的對象的集合。它定義了該集合中每個對象所共有的屬性和方法。對象是類的實例。
- **方法:**類中定義的函數。
- **類變量:**類變量在整個實例化的對象中是公用的。類變量定義在類中且在函數體之外。類變量通常不作爲實例變量使用。
- **數據成員:**類變量或者實例變量用於處理類及其實例對象的相關的數據。
- **方法重寫:**如果從父類繼承的方法不能滿足子類的需求,可以對其進行改寫,這個過程叫方法的覆蓋(override),也稱爲方法的重寫。
- **局部變量:**定義在方法中的變量,只作用於當前實例的類。
- **實例變量:**在類的聲明中,屬性是用變量來表示的。這種變量就稱爲實例變量,是在類聲明的內部但是在類的其他成員方法之外聲明的。
- **繼承:**即一個派生類(derived class)繼承基類(base class)的字段和方法。繼承也允許把一個派生類的對象作爲一個基類對象對待。例如,有這樣一個設計:一個Dog類型的對象派生自Animal類,這是模擬"是一個(is-a)"關係(例圖,Dog是一個Animal)。
- **實例化:**創建一個類的實例,類的具體對象。
- **對象:**通過類定義的數據結構實例。對象包括兩個數據成員(類變量和實例變量)和方法。
類的定義和使用
面向對象設計的思想,先抽象出類,然後再根據類創建實例。
class ClassName(object):
"dicstring"
class_statement
類的命名,大駝峯式
所謂大駝峯就是變量名稱的單詞的首字母大寫。
創建一個類
class MyFirstClass:
pass
類的作用是模板。我們可以在創建實例的時候,把一些我們認爲必須要綁定的屬性填寫進去。這時就通過特殊的__init__
方法。在創建實例的時候,綁定相關的屬性。比如前面的name,score。
class Student:
school = "zucc"
def __init__(self, name, score):
self.name = name
self.score = score
stu1 = Student("liyue", 99) # 實例化
print((stu1.name, stu1.score, stu1.school))
和普通函數相比,在類中定義方法時,第一個參數必須是self
。除第一個參數外,其他的和普通函數沒有什麼區別。
self 代表的是實例,而非類。
__init__
方法
- 1.爲對象初始化自己獨有的特徵
- 2.該方法中可以有任意的代碼,但是一定不可以有返回值。
數據封裝
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
def find_score(self):
print(self.name, ":", self.score)
stu1 = Student("Tom", 99)
stu1.find_score()
print(type(stu1))
我們通過__init__()
讓stu1實例本身就擁有了相關數據,如果要訪問這些數據,我們可以直接在Student類的內部去定義相關的函數來訪問數據,以此“封裝”數據。
這些封裝數據的函數和Student類本身是關聯起來的,他們被稱之爲方法。
類的兩個作用:
-
屬性引用
類名.屬性
-
實例化
- 類名加上一個括號就是實例化,他能夠自動觸發
__init__
函數的運行,進而爲每個實例定製自己的特徵。
- 類名加上一個括號就是實例化,他能夠自動觸發
類屬性的補充
類屬性的查看
- 1.dir(類名)
- 返回一個列表
- 2.
類名.__dict__
- 返回一個字典,key爲屬性名,value爲屬性值。
特殊的類屬性
類名.__name__ # 返回類的名稱
類名.__doc__ # 類的文檔字符串
類名.__base__ # 類的第一個父類
類名.__bases__ # 類的所有父類構成的元組
類名.__modul__ # 類定義所在的模塊
類名.__class__ # 實例對應的類
類名.__dict__ # 返回一個字典,key爲屬性名,value爲屬性值
總結:
class ClassName:
def __init__(self, para1, para2, ...):
self.obj_attr = para1
self.obj_sttr = para2
def 方法名(self):
pass
def 方法名2(self):
pass
obj = ClassName(para1, para2, ...)
# 對象的實例化,代表一個具體的東西
# ClassName():調用__init__
# 括號內傳參,無需傳入self,參數一一對應
# 結果返回一個對象obj
obj.obj_attr # 查看對象的屬性
obj.方法名1 # 調用類的方法
對象之間的交互
假如說現在定義兩個類,Person,Dog。
class Person:
def __init__(self, name, aggressivity, life_value):
self.name = name
self.aggressivity = aggressivity
self.life_value = life_value
def attack(self, dog):
dog.life_value -= self.aggressivity
class Dog:
def __init__(self, name, breed, aggressivity, life_value):
self.name = name
self.breed = breed
self.aggressivity = aggressivity
self.life_value = life_value
def bite(self, people):
people.life_value -= self.aggressivity
per = Person("Jack", 10, 1000)
dog = Dog("jeorry", "Husky", 8, 1000)
while True:
per.attack(dog)
if dog.life_value <= 0:
print(per.life_value)
break
dog.bite(per)
if per.life_value <= 0:
print(dog.life_value)
break
類命名空間與對象、實例的空間
創建一個類就會創建一個類的名稱空間,用來存儲我們定義的所有的變量名。這些名字就是屬性。
類的屬性有兩種:
- 靜態屬性
- 直接在類中定義的變量
- 動態屬性
- 在類中定義的方法
靜態屬性是共享給所有對象的
動態屬性是綁定到所有對象的
class Student:
school = 'zucc'
def __init__(self, name, score):
self.name = name
self.score = score
def find_score(self):
print(self.name, ":", self.score)
stu1 = Student("Tom", 99)
stu2 = Student("Jack", 89)
print(id(stu1.school))
print(id(stu2.school))
print(stu1.find_score)
print(stu2.find_score)
print(Student.find_score)
#2957132795104
#2957132795104
#<bound method Student.find_score of <__main__.Student object at 0x000002B089C853C8>>
#<bound method Student.find_score of <__main__.Student object at 0x000002B089C85978>>
#<function Student.find_score at 0x000002B089CE81E0>
函數的三大特性
- 繼承
- 多態
- 封裝
繼承
在面向對象編程中,當我們定義一個新類的時候,可以從某個現有的類繼承,新的類就被稱爲子類(SubClass),而被繼承的類則被稱爲基類、父類、超類(Base Class、Father Class、Super Class)
比如,我們定義一個動物類(Animal),其有一個run()方法如下:
class Animal(object):
def run(self):
print("Animai is running.")
class Animal2(object):
pass
class Dog(Animal): # Dog類繼承自Animal類
pass
class Husky(Animal, Animal2): # 多繼承,用逗號分隔開
pass
dog = Dog() # Dog類創建實例
dog.run() # Dog類內未定義方法,實例直接調用父類的方法
print(Husky.__bases__) # 繼承的查看
#Animai is running.
#(<class '__main__.Animal'>, <class '__main__.Animal2'>)
繼承的查看
ClassName.__bases__
如果不指定基類,Python類會默認繼承object類,object是所有Python類的基類,提供一些常見方法的實現。
多態
當子類和父類存在相同的方法時,子類的方法會覆蓋父類的方法,在運行代碼時,總會調用子類和父類同名的方法。
這樣,就是繼承的另一個好處,多態。
class Animal(object):
def run(self):
print("Animai is running.")
class Animal2(object):
pass
class Dog(Animal): # Dog類繼承自Animal類
def run(self):
print("Dog is running.")
class Cat(Animal):
def run(self):
print("Cat is running.")
class Husky(Animal, Animal2): # 多繼承,用逗號分隔開
pass
dog = Dog() # Dog類創建實例
dog.run() # Dog類內未定義方法,實例直接調用父類的方法
# Dog is running.
理解多態,首先要對數據類型再進行說明。定義一個類的時候,實際上就是定義了一種數據類型。我們自定義的數據類型和Python自帶的數據類型,比如str、list、dict沒什麼區別。
用isinstance()來判斷某個變量是否是某個類型
對於一個變量,我們只要知道他的父類型,無需確切知道子類型,就可以放心調用相關的方法。運行時,具體的方法是作用在子類型上還是父類型上,由我們運行的對象決定。
也就是說,調用時,只管調用,不管細節。當我們新增一個子類時,只要保證相關的方法編寫正確,就不用管原來的代碼時如何調用的。
—>”開閉“原則
- 對拓展開放:允許新增子類
- 對修改封閉:不需要修改依賴父類類型的函數。
總結:
繼承可以一級一級的繼承下來,類比人類,就好比,爺爺奶奶到父母,再到子女
任何類都可以追溯到根類object。
私有屬性
在類的內部,可以有屬性和方法,而外部代碼可以通過直接調用實例變量和方法來操作數據。這樣,隱藏內部的複雜邏輯。
比如Student類:
class Student:
school = 'zucc'
def __init__(self, name, score):
self.name = name
self.score = score
def find_score(self):
print(self.name, ":", self.score)
stu1 = Student("Tom", 99)
stu2 = Student("Jack", 89)
print(stu1.score)
stu1.score = 97
print(stu1.score)
# 99
# 97
從這可以看出,外部代碼可以自由修改一個實例的屬性(name,score)
如果要讓內部屬性不被外部訪問,我們可以在屬性的名稱前面加兩個下劃線。
在Python中,實例的變量名如果以雙下劃線開頭,就變成了一個私有變量,只有內部可以訪問,外部不能訪問。
封裝
隱藏對象的屬性和實現細節,僅對外提供公共訪問的方式。
這樣做的優點在於:
1.可以將變化格式;
2.便於使用;
3.提高整個數據的安全性;
4.提高複用性。
封裝的原則是:
- 將不需要對外提供的內容隱藏起來;
- 隱藏屬性,提供公共方法對其進行訪問。
—> 私有方法,私有變量 —> 私有屬性
用雙下劃線開頭的方式將屬性隱藏,設爲私有的。
class Student: # 定義父類
school = 'zucc'
def __init__(self, name, score):
self.__name = name
self.__score = score
def get_name(self):
return self.__name
def get_score(self):
return self.__score
def set_score(self, score):
self.__score = score
def find_score(self):
print(self.__name, ":", self.__score)
stu1 = Student("Tom", 99)
print(stu1.get_score())
stu1.set_score(50)
print(stu1.get_score())
#99
#50
鴨子類型
鴨子類型不要求有嚴格的繼承關係,一個對象,只要“看起來像鴨子,走起路來還是像鴨子”
也就是說,如果要編寫現有對象的自定義版本,可以繼承該對象,也可以創建一個外觀和行爲像的對象,但與其無任何關係的全新對象。
比方說,利用標準庫中定義的各種“與文件類似的”的對象,儘管這些對象的工作方式像文件,但他們並沒有繼承內置文件對象的方法。
class TestFile:
def read(self):
pass
def write(self):
pass
class OperFile:
def read(self):
pass
def write(self):
pass
靜態方法和類方法
靜態方法
- 通過裝飾器@staticmethod 來進行裝飾。靜態方法既不需要傳遞類對象,也不需要傳遞實例對象
- 靜態方法也可以通過
實例對象
和類對象
去訪問。
class Dog:
type = '狗'
def __init__(self):
name = None
# 靜態方法
@staticmethod
def introduce(): # 靜態方法不會自動傳遞實例對象和類對象
print("犬科哺乳動物,屬於肉食目.")
dog = Dog()
Dog.introduce()
dog.introduce()
靜態方法是類中的函數,不需要實例。
靜態方法主要是用來存放邏輯性的代碼,邏輯上屬於類,但和類本身沒有關係,也就是說在靜態方法中,不會涉及類中屬性和方法的操作。
—> 可以理解爲,靜態方法是一個獨立的,單純的函數,僅僅是託管於某個類的命名空間中,便於維護和管理。
使用場景:
- 當方法中
既不需要使用實例對象
(如實例對象,實例屬性),也不需要使用類對象
(如類屬性、類方法、創建實例等)時,定義靜態方法 取消不需要的參數傳遞
,有利於減少不必要的內存佔用和性能消耗
- 如果在類外面寫一個同樣的函數來做這些事,打亂了邏輯關係,導致代碼維護困難,使用靜態方法。
類方法
- 類對象所擁有的方法
- 需要用裝飾器@classmethod 來標識其爲類方法。
- 對於類方法,第一個參數必須是類對象,一般以cls 作爲第一個參數
class Dog:
__type = "狗"
# 類方法,用classmethod來進行裝飾
@classmethod
def get_type(cls):
return cls.__type
print(Dog.get_type())
# 狗
使用場景:
- 當方法中需要使用類對象(如訪問私有類屬性等)時,定義類方法。
- 類方法一般和類屬性配合使用。
注意:
類中定義了同名的對象方法,類方法以及靜態方法時,調用方法會優先執行最後定義的方法。
demo:
class Dog:
def demo_method(self):
print("對象方法")
@classmethod
def demo_method(cls):
print("類方法")
@staticmethod
def demo_method(): # 最後被定義,調用時優先執行
print("靜態方法")
dog = Dog()
Dog.demo_method()
dog.demo_method()
# 靜態方法
# 靜態方法
property
概述
在Python中主要爲屬性提供一個便利的操作方式。
如果我們現在需要設計一個銀行賬戶類,這個類中包含賬戶人的姓名,餘額。
簡單的實現:
class Account(object):
def __init__(self, name, money):
self.name = name
self.money = money
問題:
不安全(設計簡單方便,所有的屬性,外部都可以訪問修改,非常不安全)
改進1 隱藏實現細節
對於賬戶信息而言,金額不允許讓用戶直接修改。如果修改,只能去窗口辦理。
程序的實現該如何去做?
在使用對象時,儘量不要讓使用者直接操作對象中的屬性,因爲直接操作會帶來安全隱患。
這個時候,考慮私有屬性
。
class Account(object):
def __init__(self, name, money):
self.__name = name
self.__money = money
代碼改進以後,將所有的屬性設計成私有屬性後。確實從外部使用時,不知道內部的屬性是什麼,不能直接修改對象,隱藏了實現的細節。
但一個新的問題,如果確實需要對這兩個屬性進行修改,該怎麼辦?
改進2 提供一個精確的訪問
添加方法,訪問私有屬性。
class Account(object):
def __init__(self, name, money):
self.__name = name
self.__balance = money
def get_name(self):
return self.__name
def set_balance(self, money):
self.__balance = money
def get_balance(self):
return self.__balance
經過修改,外部使用這個類的對象時,想使用對象中的屬性,只能通過類中提供的 set/get 接口來操作,提高了程序的安全性。
這樣,程序基本達到了設計需求,但是能不能更加完善呢?
如果在使用這個類的對象過程中,由於誤操作,傳入了不正常的數據,導致數據異常。該如何以避免這種情況發生呢?
比如:設置金額時出現了負數,或字符串,或其它類型的對象。
改進3 保證數據的有效性
set_balance方法中,對傳入的數據進行有效判斷,如果是無效數據,提示用戶出錯。
class Account(object):
def __init__(self, name, money):
self.__name = name
self.__balance = money
def get_name(self):
return self.__name
def set_balance(self, money):
if isinstance(money, int):
if money > 0:
self.__balance = money
else:
raise ValueError("輸入金額不正確")
else:
raise ValueError("輸入金額不是數字")
def get_balance(self):
return self.__balance
經過幾版本的迭代,程序看上去越來越健壯,安全性也越來越高。
但是使用過程中,可不可以更精煉一些?
屬性操作。
property類
在Python中,提供了一個property類,通過對創建這個類的對象的設置,在使用對象的私有屬性時,可以不再使用屬性的函數的調用方式,而是向普通的公有屬性一樣去使用屬性,爲開發者提供便利。
property(fget=None, fset=None, fdel=None, doc=None) # property attribute
property 是一個類,
__init__
方法由四個參數組成,實例後返回一個用來操作屬性的對象
- 參數一:屬性的獲取方法
- 參數二:屬性的設置方法
- 參數三:屬性的刪除方法
- 參數四:屬性描述
class Account(object):
def __init__(self, name, money):
self.__name = name
self.__balance = money
def __get_name(self):
return self.__name
def set_balance(self, money):
if isinstance(money, int):
if money > 0:
self.__balance = money
else:
raise ValueError("輸入金額不正確")
else:
raise ValueError("輸入金額不是數字")
def get_balance(self):
return self.__balance
name = property(__get_name)
balance = property(get_balance, set_balance)
ac = Account('Tom', 10000)
print(ac.name)
print(ac.balance)
ac.balance = 1000
print(ac.balance)
# Tom
# 10000
# 1000
# 未使用property
ac = Account('Tom', 10000) # 無法訪問name,get_name 方法爲私有方法
print(ac.get_balance())
ac.set_balance(1000)
print(ac.get_balance())
# 10000
# 1000
通過 property 類實例對象以後,在使用對象中的屬性時,就可以像使用普通公有屬性一樣來調用,但是實際調用的還是 set/get 方法。 在實例 property 對象時,不是所有的參數都需要寫,比如示例中的 name 只提供了 get 方法,並且是一個私有的方法。這樣就完全隱藏了內部的實現細節 。通過 property 類實例對象以後,在使用對象中的屬性時,就可以像使用普通公有屬性一樣來調用,但是實際調用的還是 set/get 方法。 在實例 property 對象時,不是所有的參數都需要寫,比如示例中的 name 只提供了 get 方法,並且是一個私有的方法。這樣就完全隱藏了內部的實現細節 。
改寫property形式
class Account(object):
def __init__(self, name, money):
self.__name = name
self.__balance = money
@property
def name(self):
return self.__name
@property
def balance(self):
return self.__balance
@balance.setter
def balance(self, money):
if isinstance(money, int):
if money > 0:
self.__balance = money
else:
raise ValueError("輸入金額不正確")
else:
raise ValueError("輸入金額不是數字")
ac = Account('Tom', 10000)
print(ac.name)
print(ac.balance)
ac.balance = 1000
print(ac.balance)
# Tom
# 10000
# 1000
self
如果對象的方法中需要使用該對象的屬性,該怎麼辦?
- 關鍵字
self
主要用於對象方法中,表示調用該方法的對象。 - 在方法中使用self,可以獲取到調用當前方法的對象,進而獲取該對象的屬性和方法。
調用對象的方法時,爲什麼不需要設置self對應的參數?
- 某個對象調用其方法時,Python解釋器會把這個對象作爲第一個參數傳遞給方法,所以,開發者只需要在定義的時候預留第一個參數
self
即可。
class Cat:
# 方法
def introduce(self):
print("name is: %s, age is: %d" % (self.name, self.age))
cat = Cat()
cat.name = 'liyue'
cat.age = 11
cat.introduce()
# name is: liyue, age is: 11
方法內定義屬性
- 使用self操作屬性和對象的變量名在效果上類似。如果屬性在賦值時還沒被定義。就會自動定義一個屬性並賦值。
class Cat:
def introduce(self):
self.type = "小型動物"
cat = Cat()
cat.introduce()
print(cat.type)
# 小型動物
__new__
方法
-
創建對象時,系統會自動調用
__new__
方法。 -
開發者可以使用
__new__
方法來自定義對象的創建過程。 -
__new__
至少要有一個參數cls,代表要實例化的類,此參數在實例化時由Python解釋器自動提供 -
__new__
必須要有返回值,返回實例化出來的實例,這點在自己實現__new__
時要特別注意,可以return父類__new__
出來的實例,或者直接是object的__new__
出來的實例 -
__init__
有一個參數self,就是這個__new__
返回的實例,__init__
在__new__
的基礎上可以完成一些其它初始化的動作,__init__
不需要返回值 -
如果創建對象時傳遞了自定義參數,且重寫了new方法,則new也必須 “預留” 該形參,否則init方法將無法獲取到該參數
class A(object):
def __new__(cls, x):
print('this is in A.__new__, and x is ', x)
return super(A, cls).__new__(cls)
def __init__(self, y):
print('this is in A.__init__, and y is ', y)
class C(object):
def __new__(cls, n):
print('this is in C.__new__, and n is ', n)
return super(C, cls).__new__(cls)
def __init__(self, a):
print('this is in C.__init__, and a is ', a)
class B(A):
def __new__(cls, z):
print('this is in B.__new__, and z is ', z)
return A.__new__(cls, z)
def __init__(self, m):
print('this is in B.__init__, and m is ', m)
# class B(A):
# def __new__(cls, z):
# print 'this is in B.__new__, and z is ', z
# return object.__new__(cls)
# def __init__(self, m):
# print 'this is ni B.__init__, and m is ', m
if __name__ == '__main__':
a = A(100)
print('=' * 20)
b = B(200)
print(type(b))
__call__
方法
__call__
方法能夠讓類的實例對象,像函數一樣被調用
對象後面加括號,觸發執行
構造方法的執行是由創建對象觸發的,即:對象=類名()
而對於,__call__
方法的執行是由對象後加括號觸發的,即對象()或者類()
class A(object):
def __call__(self, x):
print('__call__ called, print x: ', x)
a = A()
a('444')
# __call__ called, print x: 444
instance 和 issubclass
isinstance(obj,cls) 檢查obj是否是類cls的對象
issubclass(Sub,Sup) 檢查Sub是否是Sup的子類
class Foo:
pass
foo = Foo()
print(isinstance(foo,Foo)) # True
print(isinstance(1,int)) # True
class Foo:
pass
class Bar(Foo):
pass
print(issubclass(Bar,Foo)) # True Bar is sub class of Foo
反射
所謂反射,是指程序可以訪問、檢測、修改本身狀態或者行爲的一種能力(自省)
在Python中,面向對象中的反射是指通過字符串的形式操作對象的相關屬性。
四個可以實現自省的函數
- hasattr
- hasattr(*args, **kwargs)
- getattr
- getattr(obj, name, default=None)
- setattr
- setattr(x, y, v)
- delattr
- delattr(x, y)
class Foo:
f = "類的靜態變量"
def __init__(self, name, age):
self.name = name
self.age = age
def say(self):
print("My name is %s, my age is %d." % (self.name, self.age))
obj = Foo("liyue", 21)
# 檢測是否含有某個屬性
print(hasattr(obj, 'name')) # True
print(hasattr(obj, 'f')) # True
print(hasattr(obj, 'say')) # True
# 獲取屬性
getattr(obj, 'say')() # My name is liyue, my age is 21.
print(getattr(obj, 'age')) # 21
# print(getattr(obj, 'hello')) # 不存在,報錯
print(getattr(obj, 'hello', 1)) # 1
# 設置屬性
print(obj.__dict__) # {'name': 'liyue', 'age': 21}
setattr(obj, 'good man', True)
setattr(obj, 'show_name', lambda self: self.name + 'good man')
print(obj.__dict__) # {'name': 'liyue', 'age': 21, 'good man': True, 'show_name': <function <lambda> at 0x000001E15EE03E18>}
print(obj.show_name(obj)) # liyuegood man
# 刪除屬性
delattr(obj, 'show_name')
print(obj.__dict__) # {'name': 'liyue', 'age': 21, 'good man': True}
# delattr(obj, 'hello') # 不存在,報錯
item系列
聯想,字符串、列表、元組可以通過索引值去引用值,字典可以通過關鍵字引用值,引用[].
class Foo:
def __init__(self, name):
self.name = name
def __getitem__(self, item):
print(self.__dict__[item])
def __setitem__(self, key, value):
self.__dict__[key] = value
def __delitem__(self, key):
print("del obj[key]時:")
self.__dict__.pop(key)
def __delattr__(self, item):
print("del obj.key時:")
self.__dict__.pop(item)
obj1 = Foo('obj1')
obj1['age'] = 18
obj1['age1'] = 19
print(obj1.__dict__) # {'name': 'obj1', 'age': 18, 'age1': 19}
del obj1['age'] # del obj[key]時:
del obj1.age1 # del obj.key時:
print(obj1.__dict__) # {'name': 'obj1'}
obj1['name'] = 'Jack'
print(obj1.__dict__) # {'name': 'Jack'}
__del__
析構方法,當對象在內存中被釋放,就會自動觸發執行。
一般不需要自定義。
因爲Python是一門高級語言,程序員在使用時無需關心內存的分配和釋放,因爲此工作都是交給Python解釋器來執行,所以,析構函數的調用是由解釋器在進行垃圾回收時自動觸發執行的。
class Foo:
def __del__(self):
print("執行Del函數了。")
obj = Foo()
del obj # 執行Del函數了。
__str__
-
如果直接print打印對象。會看到創建對象的內存地址。
-
當我們使用print(XXX)時,輸出對象,如果對象中定義了
__str__
方法,就會打印該方法return的信息描述。
class Cat:
def __init__(self, new_name, new_age):
self.name = new_name
self.age = new_age
def __str__(self):
return '名字是 %s,年齡是%d' % (self.name, self.age)
cat = Cat('Tom', 5)
print(cat) # 名字是 Tom,年齡是5