文成小盆友python-num8 面向對象中的成員,成員修飾符,特殊成員,異常處理,設計模式之單例模式

本節主要內容:python

1.面向對象中的成員程序員

2.成員修飾符編程

3.特殊成員json

4.異常處理設計模式

5.設計模式之單例模式cookie

一.面向對象中的成員(類的成員)

類的成員總共能夠分爲3大類,每類中有不一樣的分支。app

1.總述,基本分類

以下圖所示: 類成員包括字段,方法,和屬性框架

 

2.字段

如上圖字段分爲普通字段和靜態字段,二者的使用有區別,可是最大的區別在於二者在內存中的保存位置有區別。less

普通字段屬於對象而靜態字段屬於類,在使用過程當中誰的字段就由誰來調用。ide

靜態字段和普通字段的定義以下:

 

 

在調用時分各自調用

#####類中的對象 字段
class Province:
    country = "中國"

    def __init__(self,name):
        self.name = name

    def show(self):
         print(self.name)


hn = Province("hhh")
####

hn.show()
#通常狀況下 本身訪問本身的字段成員 對象訪問本身的字段,類訪問本身的字段
print(Province.country)    ---直接用類調用其的靜態字段
print(hn.name)                --用對象來調用普通字段

##!!!! 特殊狀況下(python),也能對象去訪問。
print(hn.country)           --不建議使用

##總之 誰的字段就用誰自己來訪問。

  經過上面的例子,得知【普通字段須要經過對象來訪問】【靜態字段經過類訪問】他的應用場景,經過類建立對象時,若是每一個對象都具備相同的字段,那麼就使用靜態字段

以下圖:靜態字段在內存中保留一份,普通字段在每一個對象中都會保留,因此當有多個對象都使用的字段就設置成爲靜態字段。

 

3.方法

 方法包括:普通方法、靜態方法和類方法,三種方法在內存中都歸屬於類,區別在於調用方式不一樣。

  • 普通方法:由對象調用;至少一個self參數;執行普通方法時,自動將調用該方法的對象賦值給self
  • 類方法:由調用; 至少一個cls參數;執行類方法時,自動將調用該方法的複製給cls
  • 靜態方法:由調用;無默認參數; --(至關於普通的函數,爲了可維護性和代碼的可讀性寫到類中稱爲類方法)
class Foo:

    def __init__(self, name):
        self.name = name

    def ord_func(self):
        """ 定義普通方法,至少有一個self參數 """

        # print self.name
        print '普通方法'

    @classmethod      #--關鍵標識
    def class_func(cls):
        """ 定義類方法,至少有一個cls參數 """ 

        print '類方法'

    @staticmethod    #--關鍵標識--
    def static_func():
        """ 定義靜態方法 ,無默認參數"""

        print '靜態方法'


# 調用普通方法
f = Foo()
f.ord_func()

# 調用類方法
Foo.class_func()      

# 調用靜態方法
Foo.static_func()

 相同點: 對於全部的方法而言都屬於類,因此在內存中也保留一份

不一樣點:調用者不一樣(對象調用&類直接調用),調用時自動傳入的參數不一樣(self,cls,無)

4. 屬性

Python中的屬性實際上是普通方法的變種,必須掌握他的定義和調用方式

# ############### 定義 ###############
class Foo:

    def func(self):
        pass

    # 定義屬性
    @property    #--關鍵標識--
    def prop(self):
        pass
# ############### 調用 ###############
foo_obj = Foo()

foo_obj.func()
foo_obj.prop   #調用屬性    

由屬性的定義和調用要注意一下幾點:

    • 定義時,在普通方法的基礎上添加 @property 裝飾器;
    • 定義時,屬性僅有一個self參數
    • 調用時,無需括號
                 調用方法:foo_obj.func()
                 調用屬性:foo_obj.prop

 訪問屬性時能夠製造出和訪問字段徹底相同的假象,若是Python中沒有屬性,方法徹底能夠代替其功能。

定義屬相的方法:

  • 裝飾器 即:在方法上應用裝飾器
  • 靜態字段 即:在類中定義值爲property對象的靜態字段

裝飾器方式:在類的普通方法上應用@property裝飾器,在python的經典類和新式類中,新式類的屬性比經典類的屬性豐富

經典類,具備一種@property裝飾器(如上一步實例)

# ############### 定義 ###############    
class Goods:

    @property
    def price(self):
        return "wupeiqi"
# ############### 調用 ###############
obj = Goods()
result = obj.price  # 自動執行 @property 修飾的 price 方法,並獲取方法的返回值

新式類,具備三種@property裝飾器

# ############### 定義 ###############
class Goods(object):

    @property
    def price(self):
        print '@property'

    @price.setter
    def price(self, value):
        print '@price.setter'

    @price.deleter
    def price(self):
        print '@price.deleter'

# ############### 調用 ###############
obj = Goods()

obj.price          # 自動執行 @property 修飾的 price 方法,並獲取方法的返回值

obj.price = 123    # 自動執行 @price.setter 修飾的 price 方法,並將  123 賦值給方法的參數

del obj.price      # 自動執行 @price.deleter 修飾的 price 方法

靜態字段方式,建立值爲property對象的靜態字段

當使用靜態字段的方式建立屬性時,經典類和新式類無區別

class Foo:

    def get_bar(self):
        return 'wupeiqi'

    BAR = property(get_bar)

obj = Foo()
reuslt = obj.BAR        # 自動調用get_bar方法,並獲取方法的返回值
print reuslt

property的構造方法中有個四個參數

  • 第一個參數是方法名,調用 對象.屬性 時自動觸發執行方法
  • 第二個參數是方法名,調用 對象.屬性 = XXX 時自動觸發執行方法
  • 第三個參數是方法名,調用 del 對象.屬性 時自動觸發執行方法
  • 第四個參數是字符串,調用 對象.屬性.__doc__ ,此參數是該屬性的描述信息

 因爲靜態字段方式建立屬性具備三種訪問方式,咱們能夠根據他們幾個屬性的訪問特色,分別將三個方法定義爲對同一個屬性:獲取、修改、刪除

class Foo:

    def get_bar(self):
        return 'wupeiqi'

    # *必須兩個參數
    def set_bar(self, value): 
        return return 'set value' + value

    def del_bar(self):
        return 'wupeiqi'

    BAR = property(get_bar, set_bar, del_bar, 'description...')

obj = Foo()

obj.BAR              # 自動調用第一個參數中定義的方法:get_bar
obj.BAR = "alex"     # 自動調用第二個參數中定義的方法:set_bar方法,並將「alex」看成參數傳入
del Foo.BAR          # 自動調用第三個參數中定義的方法:del_bar方法
obj.BAE.__doc__      # 自動獲取第四個參數中設置的值:description...

Python WEB框架 Django 的視圖中 request.POST 就是使用的靜態字段的方式建立的屬性 。

class WSGIRequest(http.HttpRequest):
    def __init__(self, environ):
        script_name = get_script_name(environ)
        path_info = get_path_info(environ)
        if not path_info:
            # Sometimes PATH_INFO exists, but is empty (e.g. accessing
            # the SCRIPT_NAME URL without a trailing slash). We really need to
            # operate as if they'd requested '/'. Not amazingly nice to force
            # the path like this, but should be harmless.
            path_info = '/'
        self.environ = environ
        self.path_info = path_info
        self.path = '%s/%s' % (script_name.rstrip('/'), path_info.lstrip('/'))
        self.META = environ
        self.META['PATH_INFO'] = path_info
        self.META['SCRIPT_NAME'] = script_name
        self.method = environ['REQUEST_METHOD'].upper()
        _, content_params = cgi.parse_header(environ.get('CONTENT_TYPE', ''))
        if 'charset' in content_params:
            try:
                codecs.lookup(content_params['charset'])
            except LookupError:
                pass
            else:
                self.encoding = content_params['charset']
        self._post_parse_error = False
        try:
            content_length = int(environ.get('CONTENT_LENGTH'))
        except (ValueError, TypeError):
            content_length = 0
        self._stream = LimitedStream(self.environ['wsgi.input'], content_length)
        self._read_started = False
        self.resolver_match = None

    def _get_scheme(self):
        return self.environ.get('wsgi.url_scheme')

    def _get_request(self):
        warnings.warn('`request.REQUEST` is deprecated, use `request.GET` or '
                      '`request.POST` instead.', RemovedInDjango19Warning, 2)
        if not hasattr(self, '_request'):
            self._request = datastructures.MergeDict(self.POST, self.GET)
        return self._request

    @cached_property
    def GET(self):
        # The WSGI spec says 'QUERY_STRING' may be absent.
        raw_query_string = get_bytes_from_wsgi(self.environ, 'QUERY_STRING', '')
        return http.QueryDict(raw_query_string, encoding=self._encoding)
    
    # ############### 看這裏看這裏  ###############
    def _get_post(self):
        if not hasattr(self, '_post'):
            self._load_post_and_files()
        return self._post

    # ############### 看這裏看這裏  ###############
    def _set_post(self, post):
        self._post = post

    @cached_property
    def COOKIES(self):
        raw_cookie = get_str_from_wsgi(self.environ, 'HTTP_COOKIE', '')
        return http.parse_cookie(raw_cookie)

    def _get_files(self):
        if not hasattr(self, '_files'):
            self._load_post_and_files()
        return self._files

    # ############### 看這裏看這裏  ###############
    POST = property(_get_post, _set_post)
    
    FILES = property(_get_files)
    REQUEST = property(_get_request)

Django源碼
源碼

因此,定義屬性共有兩種方式,分別是【裝飾器】和【靜態字段】,而【裝飾器】方式針對經典類和新式類又有所不一樣。

二.成員修飾符

私有成員和公有成員的定義不一樣:私有成員命名時,前兩個字符是下劃線。(特殊成員除外,例如:__init__、__call__、__dict__等)

class C:
 
    def __init__(self):
        self.name = '公有字段'
        self.__foo = "私有字段"

私有成員和公有成員的訪問限制不一樣

靜態字段

  • 公有靜態字段:類能夠訪問;類內部能夠訪問;派生類中能夠訪問
  • 私有靜態字段:僅類內部能夠訪問;

普通字段

  • 公有普通字段:對象能夠訪問;類內部能夠訪問;派生類中能夠訪問
  • 私有普通字段:僅類內部能夠訪

例子以下:

#成員修飾符
#公有的   私有的

1.公有的外部能夠訪問

class Foo():
    def __init__(self,name):
        self.name = name

    def f1(self):
        print(self.name)

obj = Foo("zhaowenche")
print(obj.name)        ##外部訪問公有的字段是能夠訪問的
obj.f1()
##2.私有的外部不能訪問內部能夠訪問
class Foo():
    __cc = "this is cc"
    def __init__(self,name):
        self.__name = name

    def f1(self):
        print(self.__name)

    def f2(self):
        print(Foo.__cc)

obj = Foo("zhaowenche")
#print(obj.name)        ##外部訪問私有字段是報錯的
obj.f1()
obj.f2()

#3.私有方法,外部不能夠調用(即便他的子類,派生類等也是不能夠調用的)
class Foo():
    __cc = "this is cc"
    def __init__(self,name):
        self.__name = name

    def __f1(self):
        print(self.__name)

    def f2(self):
        print(Foo.__cc)

    def f3(self):
        Foo.__f1(self)

obj = Foo("zhaowenche")
#print(obj.name)        ##外部調用私有方法是有問題的
#obj.f1()
obj.f2()
obj.f3()                ##經過f3的內部的調用__f1()是能夠獲得值的

三.類的特殊成員

1.__call__,

2.__str__

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

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

class Foo:

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

        print '__call__'


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

3.__dict__類或對象中的全部成員

class Province:

    country = 'China'

    def __init__(self, name, count):
        self.name = name
        self.count = count

    def func(self, *args, **kwargs):
        print 'func'

# 獲取類的成員,即:靜態字段、方法、
print Province.__dict__
# 輸出:{'country': 'China', '__module__': '__main__', 'func': <function func at 0x10be30f50>, '__init__': <function __init__ at 0x10be30ed8>, '__doc__': None}

obj1 = Province('HeBei',10000)
print obj1.__dict__
# 獲取 對象obj1 的成員
# 輸出:{'count': 10000, 'name': 'HeBei'}

obj2 = Province('HeNan', 3888)
print obj2.__dict__
# 獲取 對象obj1 的成員
# 輸出:{'count': 3888, 'name': 'HeNan'}

4.__getitem__、__setitem__、__delitem__

用於索引操做,如字典。以上分別表示獲取、設置、刪除數據

#item
class Foo:
    def __init__(self,name):
        self.name = name

    def __call__(self, *args, **kwargs):
        print("this is call ---")

    def __str__(self):
        print("this obj is {}".format(self.name))
        return  "this obj is {}".format(self.name)

    def __getitem__(self, item):
        print("getitem")

    def __setitem__(self, key, value):
        print("setitem")
        print(key,value)

    def __delitem__(self, key):
        print(key)

obj = Foo("zhaowencheng")
obj["aa"]         #直接執行類中的getitem
obj["bb"] = "123" #直接執行類中的seritem
del obj["cc"]     ##直接執行類中的delitem
##################################
#訪問
######分片的方式訪問

class Foo:
    def __init__(self,name):
        self.name = name

    def __getitem__(self, item):
        print(type(item))
        print(item.start)
        print(item.stop)
        print(item.step)
        #print("getitem")

    def __setitem__(self, key, value):
        #print("setitem")
        print(type(key),type(value))
        #print(key,value)

    def __delitem__(self, key):
        print(type(key))

    def __iter__(self):
        yield  1
        yield  2
        yield  3




obj = Foo("zhaowencheng")
obj[1:2]         #直接執行類中的getitem
obj[1:2] = [123,33,4,56,56,7] #直接執行類中的seritem
del obj[1:2]     ##直接執行類中的delitem

for i in obj:
    print(i)

5.super執行其父類的方法

class C1:
    def f1(self):
        print("c1.f1")

class C2(C1):
    def f1(self):
        super(C2,self).f1()
        print("c2.f1")


obj = C2()

#obj.f1()    #直接打印除c2.f1

obj.f1()    #當super時

應用:在源碼不變的基礎上經過本身新建類而後繼承python中原有的字典類來實現有序字典

##########################實現有序字典
class MyDict(dict):

    def __init__(self):
        self.li = []
        super(MyDict,self).__init__()

    def __setitem__(self, key, value):
        self.li.append(key)
        super(MyDict,self).__setitem__(key,value)

    def __str__(self):
        tem_list = []
        for key in self.li:
            value = self.get(key)
            tem_list.append("'%s':%s"%(key,value,))
        temp_str = "{" + ",".json(tem_list)+"}"
        return temp_str
        
obj = MyDict()
obj['k1'] = 123
obj['k2'] = 456
obj['k3'] = 789
print(obj)
        

6.__doc__

表示類的描述信息

class Foo:
    """ 描述類信息,這是用於看片的神奇 """

    def func(self):
        pass

print Foo.__doc__
#輸出:類的描述信息

7.__module__ 和  __class__ 

  __module__ 表示當前操做的對象在那個模塊

  __class__     表示當前操做的對象的類是什麼

8.__del__

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

注:此方法通常無須定義,由於Python是一門高級語言,程序員在使用時無需關心內存的分配和釋放,由於此工做都是交給Python解釋器來執行,因此,析構函數的調用是由解釋器在進行垃圾回收時自動觸發執行的。

四.異常處理

在編程過程當中爲了增長友好性在程序出現bug時通常不會直接將錯誤頁面直接展示在用戶面前,而是實現一個十分友好的提示界面,異常處理用try 和 except 基本語法以下:

try:
    pass
except Exception,ex:
    pass

1.基本異常處理:

#異常處理
while True:
    num1 = input("num1:").strip()
    num2 = input("num2:").strip()

    try:
        num1 = int(num1)
        num2 = int(num2)
        result = num1 + num2
        print("兩個數字相加的結果是:%s"% result)

    except Exception as  e:
        print(e)

##單輸入爲非整數時報錯以下:
num1:w
num2:3
invalid literal for int() with base 10: 'w'   # 這裏爲報錯內容
num1:

如上的Exception爲異常處理的一種,經常使用異常種類有以下幾種:

AttributeError 試圖訪問一個對象沒有的屬性,好比foo.x,可是foo沒有屬性x
IOError 輸入/輸出異常;基本上是沒法打開文件
ImportError 沒法引入模塊或包;基本上是路徑問題或名稱錯誤
IndentationError 語法錯誤(的子類) ;代碼沒有正確對齊
IndexError 下標索引超出序列邊界,好比當x只有三個元素,卻試圖訪問x[5]
KeyError 試圖訪問字典裏不存在的鍵
KeyboardInterrupt Ctrl+C被按下
NameError 使用一個還未被賦予對象的變量
SyntaxError Python代碼非法,代碼不能編譯(我的認爲這是語法錯誤,寫錯了)
TypeError 傳入對象類型與要求的不符合
UnboundLocalError 試圖訪問一個還未被設置的局部變量,基本上是因爲另有一個同名的全局變量,
致使你覺得正在訪問它
ValueError 傳入一個調用者不指望的值,即便值的類型是正確的

經常使用異常

更多的異常總類以下:

ArithmeticError
AssertionError
AttributeError
BaseException
BufferError
BytesWarning
DeprecationWarning
EnvironmentError
EOFError
Exception
FloatingPointError
FutureWarning
GeneratorExit
ImportError
ImportWarning
IndentationError
IndexError
IOError
KeyboardInterrupt
KeyError
LookupError
MemoryError
NameError
NotImplementedError
OSError
OverflowError
PendingDeprecationWarning
ReferenceError
RuntimeError
RuntimeWarning
StandardError
StopIteration
SyntaxError
SyntaxWarning
SystemError
SystemExit
TabError
TypeError
UnboundLocalError
UnicodeDecodeError
UnicodeEncodeError
UnicodeError
UnicodeTranslateError
UnicodeWarning
UserWarning
ValueError
Warning
ZeroDivisionError

更多異常
更多異常種類

應用實例:

IndexError 下標索引超出序列邊界,好比當x只有三個元素,卻試圖訪問x[5]

dic = ["zhaowencheng", 'wangmeimei']
try:
    dic[10]
except IndexError as ex:
    print(ex)
#顯示以下:
list index out of range

Process finished with exit code 0
KeyError 試圖訪問字典裏不存在的鍵

dic = {"aaa":"111", "bbb":"222"}
try:
    dic["ccc"]

except KeyError as ex:
    print(ex)
ValueError 傳入一個調用者不指望的值,即便值的類型是正確的

s1 = 'hello'
try:
    int(s1)
except ValueError, e:
    print e

###顯示
invalid literal for int() with base 10: 'hello'

Process finished with exit code 0

2.進階異常處理:

對於上面敘述的全部例中,全部的異常處理只能處理指定的異常,如KeyError只能捕捉訪問字典中不存在的key時的異常,卻沒法捕捉ValueError的類型的異常。當捕獲不到對應的異常時

程序依然回直接報錯,達不到想要的效果以下:

s1 = 'hello'
try:
    int(s1)
except KeyError as e:
    print(e)

#因爲未能捕獲到正確的err程序依然回直接報錯,顯示以下:
Traceback (most recent call last):
  File "/Users/wenchengzhao/PycharmProjects/s13/day8/temp.py", line 328, in <module>
    int(s1)
ValueError: invalid literal for int() with base 10: 'hello'

Process finished with exit code 1

關於如上的內容應該改如何操做呢,有一種笨的方法如就是捕獲多種類型的err,

s1 = 'hello'
try:
    int(s1)
except KeyError as e:
    print(e)
except ValueError as e:
    print(e)
except IndexError as e:
    print(e)
    .
    .
    .                            #寫上更多的 err 類型。

可是這種方法很是笨拙,並且很難寫全面,其實python中有一個萬能異常Exception,他能捕獲各類異常

s1 = 'hello'
try:
    int(s1)
except Exception as e:
    print(e)

##顯示以下
invalid literal for int() with base 10: 'hello'

Process finished with exit code 0

那麼既而後萬能異常其它異常存在的意義是什麼呢?

萬能異常時這樣使用的:對於特殊處理或提醒的異常須要先定義,最後定義Exception來確保程序正常運行。以下:

s1 = 'hello'
try:
    int(s1)
except KeyError as e:
    print ("鍵錯誤")
except IndexError as e:
    print ('索引錯誤')
except Exception as e:
    print ('錯誤')

3.其它異常結構

try:
    # 主代碼塊
    pass
except KeyError,e:
    # 異常時,執行該塊
    pass
else:
    # 主代碼塊執行完,執行該塊
    pass
finally:
    # 不管異常與否,最終執行該塊
    pass

執行順序:

 

4.主動觸發異常

try:
    raise Exception('錯誤了。。。(wencheng)')
except Exception  as  e:
    print (e)

#顯示以下:
錯誤了。。。(wencheng)

Process finished with exit code 0

5.自定義異常

class ZhaowenchengException(Exception):
    def __init__(self, msg):
        self.message = msg

    def __str__(self):
        return self.message


try:
    raise ZhaowenchengException('個人異常')
except ZhaowenchengException as e:
    print(e)

#顯示以下
個人異常

Process finished with exit code 0

6斷言

python assert斷言是聲明其布爾值必須爲真的斷定,若是發生異常就說明表達示爲假。能夠理解assert斷言語句爲raise-if-not,用來測試表示式,其返回值爲假,就會觸發異常。

語法簡單以下:

# assert 條件
 
assert 1 == 1
 
assert 1 == 2

五.設計模式之  ---- 單例模式

簡單說來,單例模式(也叫單件模式)的做用就是保證在整個應用程序的生命週期中,任何一個時刻,單例類的實例都只存在一個(固然也能夠不存在)。

單例模式是一種經常使用的軟件設計模式。在它的核心結構中只包含一個被稱爲單例類的特殊類。經過單例模式能夠保證系統中一個類只有一個實例並且該實例易於外界訪問,從而方便對實例個數的控制並提供全局訪問點。

下面用pytho代碼來實現一個單例模式

class Foo:

    instance = None                 #標示用於判斷實例是否存在

    def __init__(self,name):        #構造方法
         self.name = name
    @classmethod                    #類方法
    def get_instance(cls):          #將類名做爲參數傳遞進來
        if cls.instance:            #判斷實例是否存在
            return cls.instance     #若是存在直接返回,不在建立,確保單實例
        else:
            obj = cls()
            cls.instance = obj
            return obj              #返回對象
相關文章
相關標籤/搜索