面向對象

面向對象

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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章