Python基礎19-面向對象基礎

目錄

面向對象概述

面向對象的一種實現

類的相關知識

對象的相關知識

面向對象屬性的查改增刪操作

類屬性的查改增刪

對象屬性的查改增刪

關於類、對象屬性容易混淆額或忽略的地方的說明


面向對象概述

編程發展至今有面向過程編程、函數式編程、面向對象編程三大流派,未來不知道會不會有面向數據的編程。

Python支持面向對象編程,爲什麼要面向對象呢?面向對象是個啥?我的粗淺理解,就是將一系列相干的數據、方法封裝到一起形成實例,並限制實例的數據、方法調用方式,通過對這個實例的控制達到簡化編程過程,提高代碼可讀性的目的。比如,一個之前學過C++,怎麼實現的呢,數據用struct結構封裝,成員函數就是普通的函數但是入參多了個this指針,在調用函數的時候取struct實例的地址,這樣就實現了成員函數。這就是面向對象的實現方法。Java也不例外。那麼Python呢?

面向對象的一種實現

Python中,屬性數據可以用字典封裝,函數可以作爲對象被封裝在字典裏面,函數可以嵌套從而限制其不能在作用域外被調用。根據這個思想,我們可以定義一個狗函數dog,嵌套函數定義狗的兩個方法bark、wag_tail,再用字典封裝狗的屬性。這樣就實現了面向對象的設計。調用函數的時候,傳入自己作爲參數,實際上就是類似C++裏面的this指針。

def dog(name, type, gender):
    def bark(dog):
        """
        狗會叫
        :return:
        """
        print('%s is wang!wang!' % dog['name'])
        pass

    def wag_tail(dog):
        """
        狗會搖尾巴
        :return:
        """
        print('%s wag tail %s' % (dog['name'], dog['type']))
        pass

    def init():
        """
        初始化狗,名字,品種,公母
        會叫,會搖尾巴
        :return:
        """
        dog_property = {
            'name': name,
            'type': type,
            'gender': gender,
            'bark': bark,  # 將會叫函數作爲屬性
            'wag_tail': wag_tail,  # 將會搖尾巴函數作爲屬性
        }
        return dog_property
        pass

    ret = init()
    return ret
    pass


d1 = dog('Snoopy', '哈士奇', 'female')
print(d1)
d1['bark'](d1)
d1['wag_tail'](d1)
# {'name': 'Snoopy', 'type': '哈士奇', 'gender': 'femal', 'bark': <function dog.<locals>.bark at 0x00000000028A50D0>, 'wag_tail': <function dog.<locals>.wag_tail at 0x0000000003BAA620>}
# Snoopy is wang!wang!
# Snoopy wag tail 哈士奇

d2 = dog('旺財', '柴犬', 'male')
print(d2)
d2['bark'](d2)
d2['wag_tail'](d2)
# {'name': '旺財', 'type': '柴犬', 'gender': 'mail', 'bark': <function dog.<locals>.bark at 0x0000000003BAA6A8>, 'wag_tail': <function dog.<locals>.wag_tail at 0x0000000003BAA730>}
# 旺財 is wang!wang!
# 旺財 wag tail 柴犬

類的相關知識

類描述一類事物共有的數據(屬性)和動作(方法)。類本身是沒有具體信息的,是一個抽象的類型。類可以具體化到對象。

class Dog:
    """
    這是一個狗類
    """
    fur = 'property fur'

    def bark():
        print('from bark')
        pass

    def wag_tail(self):
        print('from wag_tail')
        pass

    pass


print(Dog)
# <class '__main__.Dog'>

# 調用類的方法
Dog.bark()
# from bark
Dog.wag_tail('123')
# from wag_tail

# 比較dir函數的輸出和類__dict__的輸出
# 特別是自定義的fur、bark、wag_tail
# dir只列出屬性的名字
# __dict__累出屬性的名字和值
print(dir(Dog))
# ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__',
# '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__',
# '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__',
# '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
# '__str__', '__subclasshook__', '__weakref__', 'bark', 'fur', 'wag_tail']
print(Dog.__dict__)
# {'__module__': '__main__',
# '__doc__': '\n    這是一個狗類\n    ',
# 'fur': 'property fur',
# 'bark': <function Dog.bark at 0x0000000003BBA620>,
# 'wag_tail': <function Dog.wag_tail at 0x0000000003BBA6A8>,
# '__dict__': <attribute '__dict__' of 'Dog' objects>,
# '__weakref__': <attribute '__weakref__' of 'Dog' objects>}


# Dog類裏面有fur屬性,可以用類名直接調用
print(Dog.fur)
# Dog的__dict__屬性,查看類的屬性字典
# 上面直接調用Dog.fur實際上也是用屬性字典實現的
print(Dog.__dict__['fur'])

print(Dog.__name__)  # 類的名字Dog
print(Dog.__doc__)  # 類的說明文檔,也就是類定義後緊跟的說明"""這是一個狗類""""
print(Dog.__bases__)  # Python支持多繼承,後面仔細研究,__bases__以元組形式列出了所以父類(<class 'object'>,)

對象的相關知識

我們在面向對象的一種實現那一節用函數實現了面向對象的設計。Python內置了面向對象編程的設計。

我們自己嵌套定義的初始化函數init,Python類裏面有內置的__init__方法來初始化,__init__必須有self入參,自動將實例本身傳給self。自動進行初始化和返回實例,因此__init__不用return。類創建實例的過程和函數調用類似。

對象只保存數據屬性,不保存方法屬性。這樣,每個對象存自己的數據,共享一份方法。

對象調用方法的時候,將對象本身傳給self參數。通過self來訪問對象各自的數據。

class Dog:
    """
    這是一個狗類
    """
    fur = 'property fur'

    def __init__(self, name, breed, gender):
        self.name = name
        self.breed = breed  # 品種
        self.gender = gender
        pass

    def bark(self):
        print('%s 汪汪!' % self.name)
        pass

    def wag_tail(self):
        print('%s 搖尾巴' % self.name)
        pass

    pass


d1 = Dog('旺財', '中華田園犬', 'male')
print(d1)
# <__main__.Dog object at 0x00000000026D9BA8>

# __dict__查看對象的屬性字典
# 對象只保存數據屬性,不保存方法
print(d1.__dict__)
# {'name': '旺財', 'type': '中華田園犬', 'gender': 'male'}

# 調用對象的屬性
# 對象有的(__init__)直接調用 name
# 對象沒有的,到外面的作用域類裏面去找 fur
print(d1.name, d1.fur)

# 對象調用方法是調用類的方法,調用時將對象傳給self參數
Dog.bark(d1)
d1.bark()
# 旺財 汪汪!

面向對象屬性的查改增刪操作

Python的面向對象支持屬性的查改增刪。類的屬性、對象的屬性都可以查改增刪。

以前用Java知道可以查、改屬性,學了Python才知道屬性還可以增、刪,而且是數據屬性和方法屬性的查改增刪,真牛逼。

類屬性的查改增刪

我們做查改增刪,對比類的屬性字典__dict__的內容和調用情況。直接看代碼例子……

class Dog:
    """
    這是一個狗類
    """
    fur = 'property fur'

    def __init__(self, name, breed, gender):
        self.name = name
        self.breed = breed  # 品種
        self.gender = gender
        pass

    def bark(self):
        print('%s 汪汪!' % self.name)
        pass

    def wag_tail(self):
        print('%s 搖尾巴' % self.name)
        pass

    def eat_food(self, food):
        print('%s 吃 %s' % (self.name, food))
        pass

    pass


d1 = Dog('旺財', '中華田園犬', 'male')
d2 = Dog('123', '中華田園犬', 'male')

# ========== 類屬性的查 ==========

print(Dog.fur)
# property fur

print('查詢', Dog.__dict__)
# 查詢
# {'__module__': '__main__',
# '__doc__': '\n    這是一個狗類\n    ',
# 'fur': 'property fur',
# '__init__': <function Dog.__init__ at 0x0000000003BAA620>,
# 'bark': <function Dog.bark at 0x0000000003BAA6A8>,
# 'wag_tail': <function Dog.wag_tail at 0x0000000003BAA730>,
# 'eat_food': <function Dog.eat_food at 0x0000000003BAA7B8>,
# '__dict__': <attribute '__dict__' of 'Dog' objects>,
# '__weakref__': <attribute '__weakref__' of 'Dog' objects>}

# ========== 類屬性的改 ==========

Dog.fur = 'modify fur'
print(Dog.fur)


# 改方法屬性
def test(self):
    print('%s 是 %s 品種' % (self.name, self.breed))
    pass


# 方法屬於類,可以在類上改方法屬性
# 修改後調用eat_food其實調用的是test
Dog.eat_food = test
d1.eat_food()
# 旺財 是 中華田園犬 品種

# 可以看到eat_food的地址已經發生了變化,變成了test函數的地址
print(test)
# <function test at 0x00000000028950D0>
print('修改', Dog.__dict__)
# 修改
# {'__module__': '__main__',
# '__doc__': '\n    這是一個狗類\n    ',
# 'fur': 'modify fur',
# '__init__': <function Dog.__init__ at 0x0000000003BAA620>,
# 'bark': <function Dog.bark at 0x0000000003BAA6A8>,
# 'wag_tail': <function Dog.wag_tail at 0x0000000003BAA730>,
# 'eat_food': <function test at 0x00000000028B50D0>,
# '__dict__': <attribute '__dict__' of 'Dog' objects>,
# '__weakref__': <attribute '__weakref__' of 'Dog' objects>}

# ========== 類屬性的增 ==========

# 增加數據屬性——項圈
Dog.ring = 'leather'


# 增加方法屬性
def play_ball(self):
    print('%s 玩球' % self.name)
    pass


# 方法屬於類,可以在類上增加方法屬性play_ball
Dog.play_ball = play_ball
d1.play_ball()
# 旺財 玩球

# 可以看到增加屬性以後__dict__裏面增加了ring屬性、play_ball方法
print('增加', Dog.__dict__)
# 增加
# {'__module__': '__main__',
# '__doc__': '\n    這是一個狗類\n    ',
# 'fur': 'modify fur',
# '__init__': <function Dog.__init__ at 0x0000000003BAA620>,
# 'bark': <function Dog.bark at 0x0000000003BAA6A8>,
# 'wag_tail': <function Dog.wag_tail at 0x0000000003BAA730>,
# 'eat_food': <function test at 0x00000000028C50D0>,
# '__dict__': <attribute '__dict__' of 'Dog' objects>,
# '__weakref__': <attribute '__weakref__' of 'Dog' objects>,
# 'ring': 'leather',
# 'play_ball': <function play_ball at 0x0000000003BAA7B8>}

# ========== 類屬性的刪 ==========

# 刪除數據屬性
del Dog.ring

# 刪除方法
# 方法屬於類,可以在類上刪除
del Dog.eat_food

# 注意比較刪除前後的屬性字典,刪除ring以後就沒有ring了,刪除以後就沒有eat_food方法了
print('刪除', Dog.__dict__)
# 刪除
# {'__module__': '__main__',
# '__doc__': '\n    這是一個狗類\n    ',
# 'fur': 'modify fur',
# '__init__': <function Dog.__init__ at 0x0000000003BAA620>,
# 'bark': <function Dog.bark at 0x0000000003BAA6A8>,
# 'wag_tail': <function Dog.wag_tail at 0x0000000003BAA730>,
# '__dict__': <attribute '__dict__' of 'Dog' objects>,
# '__weakref__': <attribute '__weakref__' of 'Dog' objects>,
# 'play_ball': <function play_ball at 0x0000000003BAA7B8>}

對象屬性的查改增刪

我們做查改增刪,對比對象的屬性字典__dict__的內容和調用情況。直接看代碼例子……

class Dog:
    """
    這是一個狗類
    """
    fur = 'property fur'

    def __init__(self, name, breed, gender):
        self.name = name
        self.breed = breed  # 品種
        self.gender = gender
        pass

    def bark(self):
        print('%s 汪汪!' % self.name)
        pass

    def wag_tail(self):
        print('%s 搖尾巴' % self.name)
        pass

    def eat_food(self, food):
        print('%s 吃 %s' % (self.name, food))
        pass

    def play_ball(self, ball):
        print('%s 玩 %s' % (self.name, ball))
        pass

    pass


d1 = Dog('旺財', '中華田園犬', 'male')
d2 = Dog('123', '中華田園犬', 'male')

# ========== 對象屬性的查 ==========

print(d1.name)
print(d2.name)
# 旺財
# 123

d1.play_ball('籃球')
d2.play_ball('籃球')
print(d1.play_ball)
# 旺財 玩 籃球
# 123 玩 籃球
# <bound method Dog.play_ball of <__main__.Dog object at 0x0000000003B9D8D0>>
# 打印d1.play_ball實際訪問的是類Dog.play_ball


print(d1.__dict__)
print(d2.__dict__)
# {'name': '旺財', 'breed': '中華田園犬', 'gender': 'male'}
# {'name': '123', 'breed': '中華田園犬', 'gender': 'male'}

# ========== 對象屬性的增 ==========

# 增加數據屬性
d1.age = 10
print(d1.__dict__)
print(d2.__dict__)
# {'name': '旺財', 'breed': '中華田園犬', 'gender': 'male', 'age': 10}
# {'name': '123', 'breed': '中華田園犬', 'gender': 'male'}

# 對象可以增加方法屬性嗎?
# 理論和技術上可以這麼做,那麼增加的方法就只屬於這個對象。
# 這違背了對象實例只有數據屬性的原則,不要這麼做!!!

# ========== 對衝屬性的改 ==========

d1.age = 17
print(d1.age)
# 17

# ========== 對象屬性的刪 ==========

del d1.age
del d2.name
print(d1.__dict__)
print(d2.__dict__)
# {'name': '旺財', 'breed': '中華田園犬', 'gender': 'male'}
# {'breed': '中華田園犬', 'gender': 'male'}

關於類、對象屬性容易混淆額或忽略的地方的說明

挺繞的,直接看代碼例子裏的註釋吧……

fur = 'yellow'


class Dog:
    """
    這是一個狗類
    """
    fur = 'property fur'
    li = ['a', 'b']
    li2 = ['x', 'y']

    def __init__(self, name, breed, gender):
        self.name = name
        self.breed = breed  # 品種
        self.gender = gender
        print('--->', fur)
        pass

    pass


# __init__裏面既不是self的也不是作用域內的ring
# 那麼就是全局的ring,和類已經沒有關係了
d1 = Dog('旺財', '中華田園犬', 'male')
d2 = Dog('123', '中華田園犬', 'male')
# ---> yellow
# ---> yellow

# 數據屬性就是屬於各自的

Dog.fur = 'yellow'
d1.fur = 'white'
d2.fur = 'gray'
print('類的毛:', Dog.fur)
print('對象d1的毛:', d1.fur)
print('對象d2的毛:', d2.fur)
# 類的毛: yellow
# 對象d1的毛: white
# 對象d2的毛: gray


# 由於li是類的屬性
# 通過d1調用li的append並沒有給對象新增任何屬性
# 所以修改的就是類的li,因此li都是['a','b','c']
d1.li.append('c')
print(Dog.li)
print(d1.li)
print(d2.li)
# ['a', 'b', 'c']
# ['a', 'b', 'c']
# ['a', 'b', 'c']

# 由於li2也是類的屬性
# 但是,d2.li2=是給d1對象新增了一個屬性,這與上面append不同
# 所以修改的是d2.li2的屬性
d1.li2 = [1, 2, 3]
print(Dog.li2)
print(d1.li2)
print(d2.li2)
# ['x', 'y']
# [1, 2, 3]
# ['x', 'y']

# 此時,再對d1和d2的li2操作,已經有本質的不同了
# d1的li2是d1自己的
# d2的li2是類的
# 這一點可以從對象的__dict__可以看出
d1.li2.append('c')
d2.li2.append('z')
print(Dog.li2)
print(d1.li2)
print(d2.li2)
print(d1.__dict__)
print(d2.__dict__)
# ['x', 'y', 'z']
# [1, 2, 3, 'c']
# ['x', 'y', 'z']
# {'name': '旺財', 'breed': '中華田園犬', 'gender': 'male', 'fur': 'white', 'li2': [1, 2, 3, 'c']}
# {'name': '123', 'breed': '中華田園犬', 'gender': 'male', 'fur': 'gray'}

 

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