1. 程式人生 > >Python--面向對象編程(2)

Python--面向對象編程(2)

報錯 能力 組織 概念 dict war 意思 統一 演示

面向對象編程--進階

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__

Python--面向對象編程(2)