python面向對象詳解

面向對象

概念

瞭解一些名詞:類、對象、實例、實例化

類:具有相同特徵的一類事物(人、狗、老虎)

對象/實例:具體的某一個事物(隔壁阿花、樓下旺財)

實例化:類——>對象的過程(這在生活中表現的不明顯,我們在後面再慢慢解釋)

在python中,用變量表示特徵,用函數表示技能,因而具有相同特徵和技能的一類事物就是‘類’,對象是則是這一類事物中具體的一個。

# 對象 = 類名()
# 過程:
    # 類名()首先會創造出一個對象,創建了一個self變量
    # 調用 __init__方法,類名()裏面的參數會被這裏接收
    # 執行init方法
    # 返回self

# 對象能做的事情:
    # 查看屬性
    # 調用方法
    # __dict__對於對象的增刪改查操作都可以通過字典的語法進行

# 類名能做的事情:
    # 實例化
    # 調用方法:只不過要自己傳遞self參數
    # 調用類中的屬性,也就是靜態屬性
    #  __dict__對於類中的名字只能看 不能其他操作

定義類

class Person:   #定義一個人類
    role = 'person'  #人的角色屬性都是人
    def walk(self):  #人都可以走路,也就是有一個走路方法,也叫動態屬性
        print("person is walking...")

類的兩個作用:

屬性引用(類名.屬性)

class Person:   #定義一個人類
    role = 'person'  #人的角色屬性都是人
    def walk(self):  #人都可以走路,也就是有一個走路方法
        print("person is walking...")


>>> print(Person.role)  #查看人的role屬性
person
>>> print(Person.walk)  #引用人的走路方法,注意,這裏不是在調用
<function Person.walk at 0x0000026422932598>

實例化:類名加括號就是實例化,會自動觸發init函數的運行,可以用它來爲每個實例定製自己的特徵

class Person:   #定義一個人類
    role = 'person'  #人的角色屬性都是人
    def __init__(self,name):
        self.name = name  # 每一個角色都有自己的暱稱;
        
    def walk(self):  #人都可以走路,也就是有一個走路方法
        print("person is walking...")


print(Person.role)  #查看人的role屬性
print(Person.walk)  #引用人的走路方法,注意,這裏不是在調用

實例化的過程就是類——>對象的過程

語法:對象名 = 類名(參數)

egg = Person('egon')  #類名()就等於在執行Person.__init__()
#執行完__init__()就會返回一個對象。這個對象類似一個字典,存着屬於這個人本身的一些屬性和方法。

print(egg.name)     #查看屬性直接 對象名.屬性名
print(egg.walk())   #調用方法,對象名.方法名()

類屬性

一:我們定義的類的屬性到底存到哪裏了?有兩種方式查看
dir(類名):查出的是一個名字列表
類名.__dict__:查出的是一個字典,key爲屬性名,value爲屬性值

二:特殊的類屬性
類名.__name__# 類的名字(字符串)
類名.__doc__# 類的文檔字符串
類名.__base__# 類的第一個父類(在講繼承時會講)
類名.__bases__# 類所有父類構成的元組(在講繼承時會講)
類名.__dict__# 類的字典屬性
類名.__module__# 類定義所在的模塊
類名.__class__# 實例對應的類(僅新式類中)

語法格式

class 類名:
    def __init__(self,參數1,參數2):
        self.對象的屬性1 = 參數1
        self.對象的屬性2 = 參數2

    def 方法名(self):pass

    def 方法名2(self):pass

對象名 = 類名(參數1,參數2)  #對象就是實例,代表一個具體的東西
                  #類名() : 類名+括號就是實例化一個類,相當於調用了__init__方法
                  #括號裏傳參數,參數不需要傳self,其他與init中的形參一一對應
                  #結果返回一個對象
對象名.對象的屬性1   #查看對象的屬性,直接用 對象名.屬性名 即可
對象名.方法名()     #調用類中的方法,直接用 對象名.方法名() 即可

類命名空間與對象、實例的命名空間

創建一個類就會創建一個類的名稱空間,用來存儲類中定義的所有名字,這些名字稱爲類的屬性

而類有兩種屬性:靜態屬性和動態屬性

  • 靜態屬性就是直接在類中定義的變量
  • 動態屬性就是定義在類中的方法

其中類的數據屬性是共享給所有對象的

>>>id(egg.role)
4341594072
>>>id(Person.role)
4341594072

而類的動態屬性是綁定到所有對象的

>>>egg.attack
<bound method Person.attack of <__main__.Person object at 0x101285860>>
>>>Person.attack
<function Person.attack at 0x10127abf8> 

在obj.name會先從obj自己的名稱空間裏找name,找不到則去類中找,類也找不到就找父類…最後都找不到就拋出異常

面向對象的組合用法

組合指的是,在一個類中以另外一個類的對象作爲數據屬性,稱爲類的組合

class Weapon:
    def prick(self, obj):  # 這是該裝備的主動技能,扎死對方
        obj.life_value -= 500  # 假設攻擊力是500

class Person:  # 定義一個人類
    role = 'person'  # 人的角色屬性都是人

    def __init__(self, name):
        self.name = name  # 每一個角色都有自己的暱稱;
        self.weapon = Weapon()  # 給角色綁定一個武器;  這一步稱之爲組合
        
egg = Person('egon')
egg.weapon.prick() 
#egg組合了一個武器的對象,可以直接egg.weapon來使用組合類中的所有方法

計算圓環周長和麪積

from math import pi

class Circle:
    '''
    定義了一個圓形類;
    提供計算面積(area)和周長(perimeter)的方法
    '''
    def __init__(self,radius):
        self.radius = radius

    def area(self):
         return pi * self.radius * self.radius

    def perimeter(self):
        return 2 * pi *self.radius


circle =  Circle(10) #實例化一個圓
area1 = circle.area() #計算圓面積
per1 = circle.perimeter() #計算圓周長
print(area1,per1) #打印圓面積和周長

class Ring:
    '''
    定義了一個圓環類
    提供圓環的面積和周長的方法
    '''
    def __init__(self,radius_outside,radius_inside):
        self.outsid_circle = Circle(radius_outside) # 這一步稱之爲組合
        self.inside_circle = Circle(radius_inside)  # 這一步稱之爲組合

    def area(self):
        return self.outsid_circle.area() - self.inside_circle.area()

    def perimeter(self):
        return  self.outsid_circle.perimeter() + self.inside_circle.perimeter()


ring = Ring(10,5) #實例化一個環形
print(ring.perimeter()) #計算環形的周長
print(ring.area()) #計算環形的面積

用組合的方式建立了類與組合的類之間的關係,它是一種‘有’的關係,比如教授有生日,教授教python課程

class BirthDate:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day

class Couse:
    def __init__(self,name,price,period):
        self.name=name
        self.price=price
        self.period=period

class Teacher:
    def __init__(self,name,gender,birth,course):
        self.name=name 
        self.gender=gender
        self.birth=birth
        self.course=course
    def teach(self): 
        print('teaching')

p1=Teacher('egon','male', 
            BirthDate('1995','1','27'), 
            Couse('python','28000','4 months')
           ) 

print(p1.birth.year,p1.birth.month,p1.birth.day) 

print(p1.course.name,p1.course.price,p1.course.period)
''' 
運行結果: 
1995 1 27 
python 28000 4 months 
'''

對象之間的交互

人狗大戰

class Person:  # 定義一個人類
    role = '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:  # 定義一個狗類
    role = '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

egg = Person('egon',10,1000)  #創造了一個實實在在的人egg
ha2 = Dog('二愣子','哈士奇',10,1000)  #創造了一隻實實在在的狗ha2
print(ha2.life_value)         #看看ha2的生命值
egg.attack(ha2)               #egg打了ha2一下
print(ha2.life_value)         #ha2掉了10點血

升級版

class Person:  # 定義一個人類
    role = 'person'  # 人的角色屬性都是人

    def __init__(self, name, aggressivity, life_value, money):
        self.name = name  # 每一個角色都有自己的暱稱;
        self.aggressivity = aggressivity  # 每一個角色都有自己的攻擊力;
        self.life_value = life_value  # 每一個角色都有自己的生命值;
        self.money = money

    def attack(self, dog):
        # 人可以攻擊狗,這裏的狗也是一個對象。
        # 人攻擊狗,那麼狗的生命值就會根據人的攻擊力而下降
        dog.life_value -= self.aggressivity


class Dog:  # 定義一個狗類
    role = '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


class Weapon:
    def __init__(self, name, price, aggrev, life_value):
        self.name = name
        self.price = price
        self.aggrev = aggrev
        self.life_value = life_value

    def update(self, obj):  # obj就是要帶這個裝備的人
        obj.money -= self.price  # 用這個武器的人花錢買所以對應的錢要減少
        obj.aggressivity += self.aggrev  # 帶上這個裝備可以讓人增加攻擊
        obj.life_value += self.life_value  # 帶上這個裝備可以讓人增加生命值

    def prick(self,get, obj):  # 這是該裝備的主動技能,扎死對方
        obj.life_value -= get.aggressivity 


lance = Weapon('長矛', 200, 6, 100)
egg = Person('egon', 10, 1000, 600)  # 創造了一個實實在在的人egg
ha2 = Dog('二愣子', '哈士奇', 10, 1000)  # 創造了一隻實實在在的狗ha2

# egg獨自力戰"二愣子"深感吃力,決定窮畢生積蓄買一把武器
if egg.money > lance.price:  # 如果egg的錢比裝備的價格多,可以買一把長矛
    lance.update(egg)  # egg花錢買了一個長矛防身,且自身屬性得到了提高
    egg.weapon = lance  # egg裝備上了長矛

print(egg.__dict__)
print(egg.money, egg.life_value, egg.aggressivity)

print(ha2.life_value)
egg.attack(ha2)  # egg打了ha2一下
print(ha2.life_value)
egg.weapon.prick(egg,ha2)  # 發動武器技能
print(ha2.life_value)  # ha2不敵狡猾的人類用武器取勝,血槽空了一半

面向對象的三大特性

繼承

繼承是一種創建新類的方式,在python中,新建的類可以繼承一個或多個父類,父類又可稱爲基類或超類,新建的類稱爲派生類或子類

查看繼承 __base__,__bases__

class ParentClass1: #定義父類
    pass

class ParentClass2: #定義父類
    pass

class SubClass1(ParentClass1): #單繼承,基類是ParentClass1,派生類是SubClass
    pass

class SubClass2(ParentClass1,ParentClass2): #python支持多繼承,用逗號分隔開多個繼承的類
    pass
    
>>> SubClass1.__bases__ #__base__只查看從左到右繼承的第一個子類,__bases__則是查看所有繼承的父類
(<class '__main__.ParentClass1'>,)
>>> SubClass2.__bases__
(<class '__main__.ParentClass1'>, <class '__main__.ParentClass2'>)

如果沒有指定基類,python的類會默認繼承object類,object是所有python類的基類,它提供了一些常見方法(如__str__)的實現。

繼承與抽象(先抽象再繼承)

抽象即抽取類似或者說比較像的部分。

抽象分成兩個層次:

1.將奧巴馬和梅西這倆對象比較像的部分抽取成類;

2.將人,豬,狗這三個類比較像的部分抽取成父類。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-VqKIYZr0-1582093358970)(D:\學習筆記\九月\1567478010290.png)]

**繼承:**是基於抽象的結果,通過編程語言去實現它,肯定是先經歷抽象這個過程,才能通過繼承的方式去表達出抽象的結構。

img

#繼承的代碼實現
class Animal:

    def eat(self):
        print("%s 吃 " %self.name)

    def drink(self):
        print ("%s 喝 " %self.name)

class Cat(Animal):

    def __init__(self, name):
        self.name = name
        self.breed = '貓'

    def climb(self):
        print('爬樹')

class Dog(Animal):

    def __init__(self, name):
        self.name = name
        self.breed='狗'

    def look_after_house(self):
        print('汪汪叫')


# ######### 執行 #########

c1 = Cat('小白家的小黑貓')
c1.eat()

c2 = Cat('小黑的小白貓')
c2.drink()

d1 = Dog('胖子家的小瘦狗')
d1.eat()

派生

當然子類也可以添加自己新的屬性或者在自己這裏重新定義這些屬性(不會影響到父類),需要注意的是,一旦重新定義了自己的屬性且與父類重名,那麼調用新增的屬性時,就以自己爲準了。

在python3中,子類執行父類的方法也可以直接用super方法.

class A:
    def hahaha(self):
        print('A')

class B(A):
    def hahaha(self):
        super().hahaha()
        #super(B,self).hahaha()
        #A.hahaha(self)
        print('B')

a = A()
b = B()
b.hahaha()
super(B,b).hahaha()

class Animal:
    '''
    人和狗都是動物,所以創造一個Animal基類
    '''
    def __init__(self, name, aggressivity, life_value):
        self.name = name  # 人和狗都有自己的暱稱;
        self.aggressivity = aggressivity  # 人和狗都有自己的攻擊力;
        self.life_value = life_value  # 人和狗都有自己的生命值;

    def eat(self):
        print('%s is eating'%self.name)

class Dog(Animal):
    '''
    狗類,繼承Animal類
    '''
    def __init__(self,name,breed,aggressivity,life_value):
        super().__init__(name, aggressivity, life_value) #執行父類Animal的init方法
        self.breed = breed  #派生出了新的屬性

    def bite(self, people):
        '''
        派生出了新的技能:狗有咬人的技能
        :param people:  
        '''
        people.life_value -= self.aggressivity

    def eat(self):
        # Animal.eat(self)
        #super().eat()
        print('from Dog')

class Person(Animal):
    '''
    人類,繼承Animal
    '''
    def __init__(self,name,aggressivity, life_value,money):
        #Animal.__init__(self, name, aggressivity, life_value)
        #super(Person, self).__init__(name, aggressivity, life_value)
        super().__init__(name,aggressivity, life_value)  #執行父類的init方法
        self.money = money   #派生出了新的屬性

    def attack(self, dog):
        '''
        派生出了新的技能:人有攻擊的技能
        :param dog: 
        '''
        dog.life_value -= self.aggressivity

    def eat(self):
        #super().eat()
        Animal.eat(self)
        print('from Person')

egg = Person('egon',10,1000,600)
ha2 = Dog('二愣子','哈士奇',10,1000)
print(egg.name)
print(ha2.name)
egg.eat()

抽象類與接口類

接口類

繼承有兩種用途:

  • 繼承基類的方法,並且做出自己的改變或者擴展(代碼重用)
  • 聲明某個子類兼容於某基類,定義一個接口類Interface,接口類中定義了一些接口名(就是函數名)且並未實現接口的功能,子類繼承接口類,並且實現接口中的功能

接口初成:手動報異常:NotImplementedError來解決開發中遇到的問題

class Payment:
    def pay(self):
        raise NotImplementedError

class Wechatpay(Payment):
    def fuqian(self,money):
        print('微信支付了%s元'%money)


p = Wechatpay()  #這裏不報錯
pay(p,200)      #這裏報錯了

借用abc模塊來實現接口

from abc import ABCMeta,abstractmethod

class Payment(metaclass=ABCMeta):  # 指定元類  將要寫規範類
    @abstractmethod   # 與上面配套使用
    def pay(self,money):
        pass


class Wechatpay(Payment):
    def fuqian(self,money):
        print('微信支付了%s元'%money)

p = Wechatpay() #不調就報錯了

接口繼承實質上是要求“做出一個良好的抽象,這個抽象規定了一個兼容接口,使得外部調用者無需關心具體細節,可一視同仁的處理實現了特定接口的所有對象”——這在程序設計上,叫做歸一化。

總結

接口提取了一羣類共同的函數,可以把接口當做一個函數的集合。

然後讓子類去實現接口中的函數。

這麼做的意義在於歸一化,什麼叫歸一化,就是只要是基於同一個接口實現的類,那麼所有的這些類產生的對象在使用時,從用法上來說都一樣。

歸一化,讓使用者無需關心對象的類是什麼,只需要的知道這些對象都具備某些功能就可以了,這極大地降低了使用者的使用難度。

比如:我們定義一個動物接口,接口裏定義了有跑、吃、呼吸等接口函數,這樣老鼠的類去實現了該接口,松鼠的類也去實現了該接口,由二者分別產生一隻老鼠和一隻松鼠送到你面前,即便是你分別不到底哪隻是什麼鼠你肯定知道他倆都會跑,都會吃,都能呼吸。

再比如:我們有一個汽車接口,裏面定義了汽車所有的功能,然後由本田汽車的類,奧迪汽車的類,大衆汽車的類,他們都實現了汽車接口,這樣就好辦了,大家只需要學會了怎麼開汽車,那麼無論是本田,還是奧迪,還是大衆我們都會開了,開的時候根本無需關心我開的是哪一類車,操作手法(函數調用)都一樣

抽象類

抽象類是一個特殊的類,它的特殊之處在於只能被繼承,不能被實例化

抽象類:規範 一般情況下 是單繼承 能實現的功能是一樣的 所以在父類中可以有一些簡單的基礎實現

	如果說**類是從**一堆**對象**中抽取相同的內容而來的,那麼**抽象類**就**是從**一堆**類**中抽取相同的內容而來的,內容包括數據屬性和函數屬性。
#一切皆文件
import abc #利用abc模塊實現抽象類

class All_file(metaclass=abc.ABCMeta):
    all_type='file'
    @abc.abstractmethod #定義抽象方法,無需實現功能
    def read(self):
        '子類必須定義讀功能'
        pass

    @abc.abstractmethod #定義抽象方法,無需實現功能
    def write(self):
        '子類必須定義寫功能'
        pass

# class Txt(All_file):
#     pass
#
# t1=Txt() #報錯,子類沒有定義抽象方法

class Txt(All_file): #子類繼承抽象類,但是必須定義read和write方法
    def read(self):
        print('文本數據的讀取方法')

    def write(self):
        print('文本數據的讀取方法')

class Sata(All_file): #子類繼承抽象類,但是必須定義read和write方法
    def read(self):
        print('硬盤數據的讀取方法')

    def write(self):
        print('硬盤數據的讀取方法')

class Process(All_file): #子類繼承抽象類,但是必須定義read和write方法
    def read(self):
        print('進程數據的讀取方法')

    def write(self):
        print('進程數據的讀取方法')

wenbenwenjian=Txt()

yingpanwenjian=Sata()

jinchengwenjian=Process()

#這樣大家都是被歸一化了,也就是一切皆文件的思想
wenbenwenjian.read()
yingpanwenjian.write()
jinchengwenjian.read()

print(wenbenwenjian.all_type)
print(yingpanwenjian.all_type)
print(jinchengwenjian.all_type)

多態

import abc
class Animal(metaclass=abc.ABCMeta): #同一類事物:動物
    @abc.abstractmethod
    def talk(self):
        pass

class People(Animal): #動物的形態之一:人
    def talk(self):
        print('say hello')

class Dog(Animal): #動物的形態之二:狗
    def talk(self):
        print('say wangwang')

class Pig(Animal): #動物的形態之三:豬
    def talk(self):
        print('say aoao')

import abc
class File(metaclass=abc.ABCMeta): #同一類事物:文件
    @abc.abstractmethod
    def click(self):
        pass

class Text(File): #文件的形態之一:文本文件
    def click(self):
        print('open file')

class ExeFile(File): #文件的形態之二:可執行文件
    def click(self):
        print('execute file')

封裝

**封裝:**隱藏對象的屬性和實現細節,僅對外提供公共訪問方式。

【封裝原則】

  • 將不需要對外提供的內容都隱藏起來;
  • 把屬性都隱藏,提供公共方法對其訪問。

在python中用雙下劃線開頭的方式將屬性隱藏起來(設置成私有的)

私有屬性

#其實這僅僅這是一種變形操作
#類中所有雙下劃線開頭的名稱如__x都會自動變形成:_類名__x的形式:

class A:
    __N=0 #類的數據屬性就應該是共享的,但是語法上是可以把類的數據屬性設置成私有的如__N,
          # 會變形爲_A__N
    def __init__(self):
        self.__X=10 #變形爲self._A__X
    def __foo(self): #變形爲_A__foo
        print('from A')
    def bar(self):
        self.__foo() #只有在類內部纔可以通過__foo的形式訪問到.

#A._A__N是可以訪問到的,即這種操作並不是嚴格意義上的限制外部訪問,僅僅只是一種語法意義上的變形

私有方法

在繼承中,父類如果不想讓子類覆蓋自己的方法,可以將方法定義爲私有的

#正常情況
>>> class A:
...     def fa(self):
...         print('from A')
...     def test(self):
...         self.fa()
... 
>>> class B(A):
...     def fa(self):
...         print('from B')
... 
>>> b=B()
>>> b.test()
from B
 

#把fa定義成私有的,即__fa
>>> class A:
...     def __fa(self): #在定義時就變形爲_A__fa
...         print('from A')
...     def test(self):
...         self.__fa() #只會與自己所在的類爲準,即調用_A__fa
... 
>>> class B(A):
...     def __fa(self):
...         print('from B')
... 
>>> b=B()
>>> b.test()
from A

用到私有概念的場景:

  • 隱藏起一個屬性,不想讓類的外部調用
  • 我想保護這個屬性,不想讓屬性隨意改變
  • 我想保護這個屬性,不被子類繼承

封裝與擴展性

#類的設計者
class Room:
    def __init__(self,name,owner,width,length,high):
        self.name=name
        self.owner=owner
        self.__width=width
        self.__length=length
        self.__high=high
    def tell_area(self): #對外提供的接口,隱藏了內部的實現細節,此時我們想求的是面積
        return self.__width * self.__length


#使用者
>>> r1=Room('臥室','egon',20,20,20)
>>> r1.tell_area() #使用者調用接口tell_area


#類的設計者,輕鬆的擴展了功能,而類的使用者完全不需要改變自己的代碼
class Room:
    def __init__(self,name,owner,width,length,high):
        self.name=name
        self.owner=owner
        self.__width=width
        self.__length=length
        self.__high=high
    def tell_area(self): #對外提供的接口,隱藏內部實現,此時我們想求的是體積,內部邏輯變了,只需求修該下列一行就可以很簡答的實現,而且外部調用感知不到,仍然使用該方法,但是功能已經變了
        return self.__width * self.__length * self.__high


#對於仍然在使用tell_area接口的人來說,根本無需改動自己的代碼,就可以用上新功能
>>> r1.tell_area()

property屬性

class People:
    def __init__(self,name,weight,height):
        self.name=name
        self.weight=weight
        self.height=height
    @property
    def bmi(self):
        return self.weight / (self.height**2)

p1=People('egon',75,1.85)
print(p1.bmi)

import math
class Circle:
    def __init__(self,radius): #圓的半徑radius
        self.radius=radius

    @property
    def area(self):
        return math.pi * self.radius**2 #計算面積

    @property
    def perimeter(self):
        return 2*math.pi*self.radius #計算周長

c=Circle(10)
print(c.radius)
print(c.area) #可以向訪問數據屬性一樣去訪問area,會觸發一個函數的執行,動態計算出一個值
print(c.perimeter) #同上
'''
輸出結果:
314.1592653589793
62.83185307179586
'''

爲什麼要用property

將一個類的函數定義成特性以後,對象再去使用的時候obj.name,根本無法察覺自己的name是執行了一個函數然後計算出來的,這種特性的使用方式遵循了統一訪問的原則

class Foo:
    def __init__(self,val):
        self.__NAME=val #將所有的數據屬性都隱藏起來

    @property
    def name(self):
        return self.__NAME #obj.name訪問的是self.__NAME(這也是真實值的存放位置)

    @name.setter
    def name(self,value):
        if not isinstance(value,str):  #在設定值之前進行類型檢查
            raise TypeError('%s must be str' %value)
        self.__NAME=value #通過類型檢查後,將值value存放到真實的位置self.__NAME

    @name.deleter
    def name(self):
        raise TypeError('Can not delete')

f=Foo('egon')
print(f.name)
# f.name=10 #拋出異常'TypeError: 10 must be str'
del f.name #拋出異常'TypeError: Can not delete'

一個靜態屬性property本質就是實現了get,set,delete三種方法

class Foo:
    @property
    def AAA(self):
        print('get的時候運行我啊')

    @AAA.setter
    def AAA(self,value):
        print('set的時候運行我啊')

    @AAA.deleter
    def AAA(self):
        print('delete的時候運行我啊')

#只有在屬性AAA定義property後才能定義AAA.setter,AAA.deleter
f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA

class Foo:
    def get_AAA(self):
        print('get的時候運行我啊')

    def set_AAA(self,value):
        print('set的時候運行我啊')

    def delete_AAA(self):
        print('delete的時候運行我啊')
    AAA=property(get_AAA,set_AAA,delete_AAA) #內置property三個參數與get,set,delete一一對應

f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA

class Goods:

    def __init__(self):
        # 原價
        self.original_price = 100
        # 折扣
        self.discount = 0.8

    @property
    def price(self):
        # 實際價格 = 原價 * 折扣
        new_price = self.original_price * self.discount
        return new_price

    @price.setter
    def price(self, value):
        self.original_price = value

    @price.deleter
    def price(self):
        del self.original_price


obj = Goods()
obj.price         # 獲取商品價格
obj.price = 200   # 修改商品原價
print(obj.price)
del obj.price     # 刪除商品原價

classmethod

當一個方法只使用了類的靜態變量時,就加上@classmethod裝飾器,默認傳cls參數

class Classmethod_Demo():
    role = 'dog'

    @classmethod
    def func(cls):
        print(cls.role)

Classmethod_Demo.func()

staticmethod

靜態方法就像正常的函數一樣,不需要傳self或cls,當然也可以傳其它參數

class Staticmethod_Demo():
    role = 'dog'

    @staticmethod
    def func():
        print("當普通方法用")

Staticmethod_Demo.func()

class A:
    __role = 'CHINA'
    @classmethod
    def show_role(cls):
        print(cls.__role)

    @staticmethod
    def get_role():
        return A.__role

    @property
    def role(self):
        return self.__role

a = A()
print(a.role)
print(a.get_role())
a.show_role()

isinstance和issubclass

isinstance(obj,cls)檢查是否obj是否是類 cls 的對象

class Foo(object):
     pass
  
obj = Foo()
  
isinstance(obj, Foo)

issubclass(sub, super)檢查sub類是否是 super 類的派生類

class Foo(object):
    pass
 
class Bar(Foo):
    pass
 
issubclass(Bar, Foo)

反射

反射主要是指程序可以訪問、檢測和修改它本身狀態或行爲的一種能力(自省)。

​ 用字符串類型的名字去操作變量

反射(對象,類,模塊)的屬性,方法

python面向對象中的反射:通過字符串的形式操作對象相關的屬性。

python中的一切事物都是對象(都可以使用反射)

四個可以實現自省的函數

hasattr

def hasattr(*args, **kwargs): # real signature unknown
    """
    Return whether the object has an attribute with the given name.
    
    This is done by calling getattr(obj, name) and catching AttributeError.
    """
    pass

getattr

def getattr(object, name, default=None): # known special case of getattr
    """
    getattr(object, name[, default]) -> value
    
    Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y.
    When a default argument is given, it is returned when the attribute doesn't
    exist; without it, an exception is raised in that case.
    """
    pass

setattr

def setattr(x, y, v): # real signature unknown; restored from __doc__
    """
    Sets the named attribute on the given object to the specified value.
    
    setattr(x, 'y', v) is equivalent to ``x.y = v''
    """
    pass

delattr

def delattr(x, y): # real signature unknown; restored from __doc__
    """
    Deletes the named attribute from the given object.
    
    delattr(x, 'y') is equivalent to ``del x.y''
    """
    pass

四個方法的演示

class Foo:
    f = '類的靜態變量'
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def say_hi(self):
        print('hi,%s'%self.name)

obj=Foo('egon',73)

#檢測是否含有某屬性
print(hasattr(obj,'name'))  # True
print(hasattr(obj,'say_hi'))  # True

#獲取屬性
n=getattr(obj,'name')   # 第二個參數必須是字符串形式
print(n)
func=getattr(obj,'say_hi')  # func現在是個綁定方法
func()  # func加上()就能運行

print(getattr(obj,'aaaaaaaa','不存在aaaaaaa'))  #報錯 如果 aaaaaaaa不存在就會打印不存在aaaaaaa

#設置屬性
setattr(obj,'sb',True)
setattr(obj,'show_name',lambda self:self.name+'sb')
print(obj.__dict__)
print(obj.show_name(obj))

#刪除屬性
delattr(obj,'age')
delattr(obj,'show_name')
# delattr(obj,'show_name111')#不存在,則報錯

print(obj.__dict__)

反射自己模塊中的變量和函數

def qqxing():
    print('qqxing')
    
year = 2019
import sys

# 反射自己模塊中的變量
print(getattr(sys.modules['__main__'],'year'))

# 反射自己模塊中的函數
getattr(sys.modules['__main__'],'qqxing')()

變量名 = input('>>>')
print(getattr(sys.modules['__main__'],變量名))

print(getattr(sys.modules[__name__],變量名))


__str____repr__

改變對象的字符串顯示__str__ %s str(),__repr__ %r repr()

自定製格式化字符串__format__

print(a)打印一個對象的時候,就是調用a.__str__

一旦被調用,就返回調用這個方法的對象的內存地址

repr是str的備胎

"""
print(obj)/'%s'%obj/str(obj) 這三種操作
實際上是內部調用了obj.__str__方法,如果__str__方法有,那麼它必定返回一個字符串
如果沒有__str__方法,就會先找本類中的__repr__方法,再沒有就去父類找
"""

format_dict={
    'nat':'{obj.name}-{obj.addr}-{obj.type}',#學校名-學校地址-學校類型
    'tna':'{obj.type}:{obj.name}:{obj.addr}',#學校類型:學校名:學校地址
    'tan':'{obj.type}/{obj.addr}/{obj.name}',#學校類型/學校地址/學校名
}
class School:
    def __init__(self,name,addr,type):
        self.name=name
        self.addr=addr
        self.type=type

    def __repr__(self):
        return 'School(%s,%s)' %(self.name,self.addr)
    def __str__(self):
        return '(%s,%s)' %(self.name,self.addr)

    def __format__(self, format_spec):
        # if format_spec
        if not format_spec or format_spec not in format_dict:
            format_spec='nat'
        fmt=format_dict[format_spec]
        return fmt.format(obj=self)

s1=School('oldboy1','北京','私立')
print('from repr: ',repr(s1))
print('from str: ',str(s1))
print(s1)

'''
str函數或者print函數--->obj.__str__()
repr或者交互式解釋器--->obj.__repr__()
如果__str__沒有被定義,那麼就會使用__repr__來代替輸出
注意:這倆方法的返回值必須是字符串,否則拋出異常
'''
print(format(s1,'nat'))
print(format(s1,'tna'))
print(format(s1,'tan'))
print(format(s1,'asfdasdffd'))

# %s %r
class B:

     def __str__(self):
         return 'str : class B'

     def __repr__(self):
         return 'repr : class B'


b=B()
print('%s'%b) # 會輸出字符串形式
print('%r'%b) # 會輸出原有形式

__len__

class A:
    def __init__(self):
        self.a = 1
        self.b = 2

    def __len__(self):
        return len(self.__dict__)
a = A()
print(len(a))

__del__

析構方法,當對象在內存中被釋放時,自動觸發執行。

class Foo:

    def __del__(self):
        print('執行我啦')

f1=Foo()
del f1
print('------->')

#輸出結果
執行我啦
------->

__call__

對象後面加括號,觸發執行。

注:構造方法的執行是由創建對象觸發的,即:對象 = 類名() ;而對於 call 方法的執行是由對象後加括號觸發的,即:對象() 或者 類()()

class Foo:

    def __init__(self):
        pass
    
    def __call__(self, *args, **kwargs):

        print('__call__')


obj = Foo() # 執行 __init__
obj()       # 執行 __call__

item系列

__getitem__\__setitem__\__delitem__

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):    # object 原生支持
        print('del obj.key時,我執行')
        self.__dict__.pop(item)

f1=Foo('sb')
f1['name']    # sb
f1['age']=18
f1['age1']=19
print(f1.age1)  # 19
f1['age']      # 18
del f1.age1     # del obj.key時,我執行
del f1['age']   # del obj[key]時,我執行
f1['name']='alex'
print(f1.__dict__)  # {'name': 'alex'}

__new__

class A:
    def __init__(self):
        self.x = 1
        print('in init function')
    def __new__(cls, *args, **kwargs):
        print('in new function')
        return object.__new__(A)

a = A()
print(a.x)

單例模式

class Singleton:
    def __new__(cls, *args, **kw):
        if not hasattr(cls, '_instance'):
            cls._instance = object.__new__(cls)
        return cls._instance

one = Singleton()
two = Singleton()

two.a = 3
print(one.a)
# 3
# one和two完全相同,可以用id(), ==, is檢測
print(id(one))
# 29097904
print(id(two))
# 29097904
print(one == two)
# True
print(one is two)

__eq__

class A:
    def __init__(self):
        self.a = 1
        self.b = 2

    def __eq__(self,obj):
        if  self.a == obj.a and self.b == obj.b:
            return True
a = A()
b = A()
print(a == b) # 正常情況比較內存地址

__hash__

class A:
    def __init__(self):
        self.a = 1
        self.b = 2

    def __hash__(self):
        return hash(str(self.a)+str(self.b))
a = A()
print(hash(a))

紙牌遊戲

from collections import namedtuple
Card = namedtuple('Card',['rank','suit'])

class FranchDeck:
    ranks = [str(n) for n in range(2,11)] + list('JQKA')
    suits = ['紅心','方板','梅花','黑桃']

    def __init__(self):
        self._cards = [Card(rank,suit) for rank in FranchDeck.ranks
                                        for suit in FranchDeck.suits]

    def __len__(self):
        return len(self._cards)

    def __getitem__(self, item):
        return self._cards[item]

    def __setitem__(self, key, value):
        self._cards[key] = value

deck = FranchDeck()
print(deck[0])
from random import choice
print(choice(deck))
print(choice(deck))

from random import shuffle
shuffle(deck)
print(deck[:5])

class Person:
    def __init__(self,name,age,sex):
        self.name = name
        self.age = age
        self.sex = sex

    def __hash__(self):
        return hash(self.name+self.sex)

    def __eq__(self, other):
        if self.name == other.name and self.sex == other.sex:return True


p_lst = []
for i in range(84):
    p_lst.append(Person('egon',i,'male'))

print(p_lst)
print(set(p_lst))  # set()依賴__hash__ 和 __eq__ 方法

發佈了30 篇原創文章 · 獲贊 83 · 訪問量 9135
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章