1. 程式人生 > >Python--面向物件程式設計(2)

Python--面向物件程式設計(2)

面向物件程式設計--進階

 

property屬性

property是一種特殊的屬性,訪問它時會執行一段功能(函式)然後返回值

 

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 ''' #注意:此時的特性area和perimeter不能被賦值 c.area=3 #為特性area賦值 ''' 丟擲異常: AttributeError: can't set attribute '''
圓的周長和麵積

為什麼要用property

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

ps:面向物件的封裝有三種方式:
【public】
這種其實就是不封裝,是對外公開的
【protected】
這種封裝方式對外不公開,但對朋友(friend)或者子類(形象的說法是“兒子”,但我不知道為什麼大家 不說“女兒”,就像“parent”本來是“父母”的意思,但中文都是叫“父類”)公開
【private】
這種封裝對誰都不公開

python並沒有在語法上把它們三個內建到自己的class機制中,在C++裡一般會將所有的所有的資料都設定為私有的,然後提供set和get方法(介面)去設定和獲取,在python中通過property方法可以實現

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('lee')
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
View Code
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
View Code
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

類方法

class Classmethod_Demo():
    role = 'dog'

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

Classmethod_Demo.func()

staticmethod

靜態方法

class Staticmethod_Demo():
    role = 'dog'

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

Staticmethod_Demo.func()

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)

反射

1 什麼是反射

反射的概念是由Smith在1982年首次提出的,主要是指程式可以訪問、檢測和修改它本身狀態或行為的一種能力(自省)。這一概念的提出很快引發了電腦科學領域關於應用反射性的研究。它首先被程式語言的設計領域所採用,並在Lisp和麵向物件方面取得了成績。

 

2 python面向物件中的反射:通過字串的形式操作物件相關的屬性。python中的一切事物都是物件(都可以使用反射)

四個可以實現自省的函式

下列方法適用於類和物件(一切皆物件,類本身也是一個物件)

 

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
hasattr
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
getattr
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
setattr
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
delattr
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'))
print(hasattr(obj,'say_hi'))

#獲取屬性
n=getattr(obj,'name')
print(n)
func=getattr(obj,'say_hi')
func()

print(getattr(obj,'aaaaaaaa','不存在啊')) #報錯

#設定屬性
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__)
四個方法的使用演示

 

class Foo(object):
 
    staticField = "old boy"
 
    def __init__(self):
        self.name = 'wupeiqi'
 
    def func(self):
        return 'func'
 
    @staticmethod
    def bar():
        return 'bar'
 
print getattr(Foo, 'staticField')
print getattr(Foo, 'func')
print getattr(Foo, 'bar')
類也是物件
import sys


def s1():
    print 's1'


def s2():
    print 's2'


this_module = sys.modules[__name__]

hasattr(this_module, 's1')
getattr(this_module, 's2')
反射當前模組成員

匯入其他模組,利用反射查詢該模組是否存在某個方法

def test():
    print('from the test')
View Code
"""
程式目錄:
    module_test.py
    index.py
 
當前檔案:
    index.py
"""

import module_test as obj

#obj.test()

print(hasattr(obj,'test'))

getattr(obj,'test')()
View Code

__str__和__repr__

改變物件的字串顯示__str__,__repr__

自定製格式化字串__format__

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'))
示例
class B:

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

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


b=B()
print('%s'%b)
print('%r'%b)
%s和%r

__del__

析構方法,當物件在記憶體中被釋放時,自動觸發執行。

注:此方法一般無須定義,因為Python是一門高階語言,程式設計師在使用時無需關心記憶體的分配和釋放,因為此工作都是交給Python直譯器來執行,所以,解構函式的呼叫是由直譯器在進行垃圾回收時自動觸發執行的。

class Foo:

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

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

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

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

f1=Foo('lyq')
f1['age']=18
f1['age1']=19
del f1.age1
del f1['age']
f1['name']='lee'
print(f1.__dict__)
示例

__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, *args, **kwargs)

a = A()
print(a.x)
示例
class Singleton:
    def __new__(cls, *args, **kw):
        if not hasattr(cls, '_instance'):
            cls._instance = object.__new__(cls, *args, **kw)
        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)
單例模式

__call__

物件後面加括號,觸發執行。

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

class Foo:

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

        print('__call__')


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

__len__

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

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

__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))
示例

__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)
示例
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('lee',i,'male'))

print(p_lst)
print(set(p_lst))
View Code

面向物件常用術語

抽象/實現

抽象指對現實世界問題和實體的本質表現,行為和特徵建模,建立一個相關的子集,可以用於 繪程式結構,從而實現這種模型。抽象不僅包括這種模型的資料屬性,還定義了這些資料的介面。

對某種抽象的實現就是對此資料及與之相關介面的現實化(realization)。現實化這個過程對於客戶 程式應當是透明而且無關的。 

封裝/介面

封裝描述了對資料/資訊進行隱藏的觀念,它對資料屬性提供介面和訪問函式。通過任何客戶端直接對資料的訪問,無視介面,與封裝性都是背道而馳的,除非程式設計師允許這些操作。作為實現的 一部分,客戶端根本就不需要知道在封裝之後,資料屬性是如何組織的。在Python中,所有的類屬性都是公開的,但名字可能被“混淆”了,以阻止未經授權的訪問,但僅此而已,再沒有其他預防措施了。這就需要在設計時,對資料提供相應的介面,以免客戶程式通過不規範的操作來存取封裝的資料屬性。

注意:封裝絕不是等於“把不想讓別人看到、以後可能修改的東西用private隱藏起來”

真正的封裝是,經過深入的思考,做出良好的抽象,給出“完整且最小”的介面,並使得內部細節可以對外透明

(注意:對外透明的意思是外部呼叫者可以順利的得到自己想要的任何功能,完全意識不到內部細節的存在)

合成

合成擴充了對類的 述,使得多個不同的類合成為一個大的類,來解決現實問題。合成 述了 一個異常複雜的系統,比如一個類由其它類組成,更小的元件也可能是其它的類,資料屬性及行為, 所有這些合在一起,彼此是“有一個”的關係。

派生/繼承/繼承結構

派生描述了子類衍生出新的特性,新類保留已存類型別中所有需要的資料和行為,但允許修改或者其它的自定義操作,都不會修改原類的定義。
繼承描述了子類屬性從祖先類繼承這樣一種方式
繼承結構表示多“代”派生,可以述成一個“族譜”,連續的子類,與祖先類都有關係。

泛化/特化

基於繼承
泛化表示所有子類與其父類及祖先類有一樣的特點。
特化描述所有子類的自定義,也就是,什麼屬性讓它與其祖先類不同。

多型與多型性

多型指的是同一種事物的多種狀態:水這種事物有多種不同的狀態:冰,水蒸氣

多型性的概念指出了物件如何通過他們共同的屬性和動作來操作及訪問,而不需考慮他們具體的類。

冰,水蒸氣,都繼承於水,它們都有一個同名的方法就是變成雲,但是冰.變雲(),與水蒸氣.變雲()是截然不同的過程,雖然呼叫的方法都一樣

自省/反射

自省也稱作反射,這個性質展示了某物件是如何在執行期取得自身資訊的。如果傳一個物件給你,你可以查出它有什麼能力,這是一項強大的特性。如果Python不支援某種形式的自省功能,dir和type內建函式,將很難正常工作。還有那些特殊屬性,像__dict__,__name__及__doc__