python週報第八週

0.本週知識點預覽

  • 類的多態
  • 面向對象中類的成員
    • 字段
    • 方法
    • 屬性
  • 成員修飾符
  • 類的特殊成員
  • 面向對象其餘方法
  • 異常梳理
  • 設計模式之單例模式

  

1.面向對象進階

1.多態

在衆多語言中,在定義函數時,有的參數類型能夠隨意傳入,有的只能傳入指定的類型。這種隨意的語言的表明就是python,而規規矩矩的傳入指定類型的參數的語言,就如java、c#、c++。下面就拿C++和python作個對比。java

如下代碼是C++代碼:python

liukai@bogon:~$ cat test1.cpp
#include <iostream>
using namespace std;
int add(int x, int y)
{
    return (x+y);
}

int main()
{
    cout<<add(3,5);
}
代碼解析:C++這類語言的函數參數都要註明傳入的類型,假如類型不對就會報錯。

如下代碼是python代碼:ios

def func(arg):

    print(arg)

func(123)
func("hello")
func([1,3,4])
func(dict)

 執行結果以下:c++

123
hello
[1, 3, 4]
<class 'dict'>
代碼解析:從上能夠看到,python的函數參數能夠是任意類型。這個就很隨意了,不過也有一點弊端,就是當別人看你的代碼的時候,會發現這個參數能夠是任意類型,就會比較困惑這個參數究竟是作什麼的。因此,有利必有弊嘛。

2.類的成員

1.字段

1.靜態字段

#靜態字段

class Province:

    country = "中國"

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

    def show(self):
        # print(self.name)
        print("經過類調用:",Province.country)
        print("類內部調用:",self.country)


hlj = Province("黑龍江")
hlj.show()
print("對象調用:",hlj.country)

執行結果以下:c#

經過類調用: 中國
類內部調用: 中國
對象調用: 中國
代碼解析:靜態字段屬於類,能夠直接由類調用:類名.靜態字段名;能夠由對象直接調用:對象名.靜態字段名;能夠在類內部調用:self.靜態字段名。

2.普通字段

class Province:

    country = "中國"

    def __init__(self,name):
        self.name = name
        self.size = 100000
        pass

    def show(self):
        print("類內部調用:",self.size)


hlj = Province("黑龍江")
hlj.show()
print("對象調用:",hlj.size)

 

執行結果以下:設計模式

類內部調用: 100000
對象調用: 100000
代碼解析:普通字段屬於對象,能夠經過類內部訪問:self.普通字段名;能夠經過對象直接訪問:對象名.普通字段名,可是不能經過類來訪問,由於對象屬於類,但類不屬於對象。

2.方法

1.普通方法

##普通方法

class Province:

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

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

p = Province("黑龍江")
p.show()

執行結果以下:app

黑龍江
代碼解析:普通方法就是代碼中的show()方法,要注意:第一個參數必須是self,self會把實例化出來的對象傳入。上個例子,self就等於"p"這個對象。

2.靜態方法

##靜態方法

class Province:

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

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


    @staticmethod
    def main(args):
        print(args)

p = Province("黑龍江")
p.main(123)
Province.main([1,3,4])

執行結果以下:函數

123
[1, 3, 4]
代碼解析:靜態方法就是上述代碼的main(args),要想把普通方法變成靜態方法,須要在方法上一行加上@staticmethod裝飾器,在把普通方法的self參數去掉便可,後邊能夠加任意參數。靜態方法能夠類直接調用:類名.靜態方法名;能夠對象調用:對象名.靜態方法名。

3.類方法

##類方法

class Province:

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

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


    @staticmethod
    def main(args):
        print(args)

    @classmethod
    def clsmain(cls,args):
        print("class method")
        print(cls)
        print(args)

p = Province("lk")
Province.clsmain("123")

執行結果以下:工具

class method
<class '__main__.Province'>
123
代碼解析:類方法屬於類,類方法的定義格式,在普通方法的上邊加上@classmethod裝飾器,而且方法的第一個參數self去掉,換成cls。這個cls默認就是類名,和普通方法的self相似。調用類方法,能夠用類直接調用:類名.類方法名;能夠經過對象調用:對象名.類方法名。

3.屬性

1.屬性基礎

#屬性

class Pager:

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

    @property
    def main(self):
        a1, a2 = divmod(self.count,10)
        if a2 == 0:
            return a1
        else:
            return a1 + 1
p = Pager(158)
result = p.main
print(result)

執行結果以下:spa

16
代碼解析:屬性的定義格式是:在方法名上一行加上@property的裝飾器。而後在調用的時候,直接類名/對象名.屬性名。

2.屬性進階

class Pager:

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

    @property
    def main(self):
        a1, a2 = divmod(self.count,10)
        if a2 == 0:
            return a1
        else:
            return a1 + 1

    @main.setter
    def main(self,value):
        print("賦值時調用的方法:",value)


    @main.deleter
    def main(self):
        print("del 時調用的方法")

p = Pager(191)
ret = p.main
print(ret)
p.main = 200
del p.main

執行結果以下:

20
賦值時調用的方法: 200
del 時調用的方法
代碼解析:在方法上一行加上@方法名.setter 的裝飾器,會添加用途:當調用方式爲:對象名.方法名 = value 時纔會調用該方法。 在方法上加上@方法名.deleter 的裝飾器,會添加用途:當調用方式爲: del 對象名.方法名 時,纔會調用改方法。 也能夠這樣定義這些特殊的屬性:方法名 = property(fget=f1,fset=f2,fdel=f3),fget、fset、fdel參數都是固定的,f一、f二、f3是方法名。效果和加裝飾器相似。代碼以下:
class Pager:

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

    def f1(self):
        return 123

    def f2(self,value):
        print(value)

    def f3(self):
        print(789)


    foo = property(fget=f1,fset=f2,fdel=f3)

p = Pager(100)

ret = p.foo
print(ret)
p.foo = "fuck"
del p.foo

執行結果以下:

123
fuck
789

3.成員修飾符

1.公有 

公有的成員包括:公有字段、公有方法、公有屬性。這些成員就是以前介紹過的

2.私有

1.私有字段

定義一個私有字段的格式爲: __xx = oo

##私有字段

class Pager:

    __cc = 123

    def __init__(self,value):
        self.__value = value

    def f1(self):
        print("類內對象調用私有靜態字段:",self.__cc)
        print("類內對象調用私有普通字段:",self.__value)


    def f2(self):
        print("類內利用類直接調用私有靜態字段:",Pager.__cc)

p = Pager("fuck")
# p.__value   ##error
p.f1()
# Pager.__cc    ##error
p.f2()

執行結果以下:

類內對象調用私有靜態字段: 123
類內對象調用私有普通字段: fuck
類內利用類直接調用私有靜態字段: 123
代碼解析:私有字段,只能在類內部調用,不能夠類、對象直接在外部調用。固然也有例外,python能夠強行調用私有字段,不過不推薦!!!
##私有字段

class Pager:

    __cc = 123

    def __init__(self,value):
        self.__value = value

    def f1(self):
        print("類內對象調用私有靜態字段:",self.__cc)
        print("類內對象調用私有普通字段:",self.__value)


    def f2(self):
        print("類內利用類直接調用私有靜態字段:",Pager.__cc)

p = Pager("fuck")
print(p._Pager__value)   ##不推薦

print(Pager._Pager__cc)    ##不推薦

這樣也能夠強行調用私有字段。不過不推薦!!!

2.私有方法

###私有方法
class Pager2:

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

    def __f1(self):
        print(123)

    @staticmethod
    def __f2():
        print(456)

    @classmethod
    def __f3(cls):
        print(789)

    def f4(self):
        self.__f1()

    def f5(self):
        Pager2.__f2()

    def f6(self):
        Pager2.__f3()

p = Pager2("fuck")
p.f4()
p.f5()
p.f6()

執行結果以下:

123
456
789
代碼解析:私有方法的定義格式:在類內部,在普通方法的前邊加上雙下劃線:__   ;私有方法只能在類內部調用,在這裏只能由對象調用類內部方法來使用。

4.類的特殊成員

1.__init__ 

### __init__

class Foo:

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

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

f = Foo("lk",25)
f.show()

執行結果以下:

lk 25
代碼解析:類的__init__方法的做用是初始化數據,叫作構造方法,方便其餘類內部方法使用。

2.__del__、__doc__、__call__、__str__、__dict__、__iter__

1.__del__

__del__叫作析構方法,當對象被回收時自動執行的方法。

2.__doc__

__doc__方法的做用是打印類內部的註釋
### __doc__
class Foo:

    """
    我是註釋!!!!
    """

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

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

f = Foo("lk",25)
print(f.__doc__)

執行結果以下:

    我是註釋!!!!
    

3.__call__

### __call__
class Foo:

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

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

    def __call__(self, *args, **kwargs):
        print("我是__call__方法")

f = Foo("lk",25)
f()

執行結果以下:

我是__call__方法
代碼解析:f 是 Foo 的一個對象,在對象f 後邊加上小括號,就會執行類內部的__call__方法。這個比較坑,很容易被忽略。

4.__str__

## __str__
class Foo:

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

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

    def __str__(self):
        return self.name

f = Foo("lk",25)
print(f)

執行結果以下:

lk
代碼解析:f 是 Foo類的一個對象,當直接print(f) 的時候,就會調用類內部的__str__方法。

 5.__dict__

__dict__方法也很是實用,它能夠看到構造方法中的全部字段,包括私有字段!

## __dict__
class Foo:

    def __init__(self,name,age,salary):
        self.name = name
        self.age = age
        self.__salary = salary

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


f = Foo("lk",25,3000)
print(f.__dict__)

執行結果以下:

{'name': 'lk', 'age': 25, '_Foo__salary': 3000}
代碼解析:在構造方法中構造了三個字段,執行__dict__,能夠以字典的形式打印出全部字段。

6.__iter__

# __item__

class Foo:

    ##必須是個迭代器才能循環,當循環一個對象時,就會執行__iter__方法.
    def __iter__(self):
        yield 2
        yield 3


f = Foo()
for i in f:
    print(i)

執行結果以下:

2
3
代碼解析:要循環一個對象,必須類內部有一個__iter__方法,且它必須是一個迭代器。才能循環。

3.__getitem__、__setitem__、__delitem__

##  __getitem__   __setitem__  __delitem__

class Foo:

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

    def __getitem__(self, item):
        # return self.age
        print("==get_item==")
        print(item)
        print(type(item))
        print(item.start,item.stop,item.step)

    def __setitem__(self, key, value):
        print("==set_item==")
        print(type(key),type(value))
        print(key.start,key.stop,key.step)

    def __delitem__(self, key):
        print("==del_item==")
        print(type(key))

執行結果以下:

==get_item==
slice(1, 4, 2)
<class 'slice'>
1 4 2
==set_item==
<class 'slice'> <class 'list'>
1 4 2
==del_item==
<class 'slice'>
代碼解析:__getitem__方法:當執行對象[] 的時候,會執行類中的__getitem__方法,[]中的內容能夠是字符串、數字(這時就沒有item.start、stop、step了),能夠是列表分片的形式。當傳入字符串、數字就是相應類型。當傳入列表分片的形式時,它是一個slice類型。 __setitem__方法:當執行對象[] = xxx 的時候,會執行類內部的__setitem__方法,這個方法有兩個參數,一個key,一個value。和__getitem__方法同樣,傳入的參數能夠是字符串、數字、列表分片,列表分片的參數纔有start、stop、step的功能。 __delitem__方法:當執行 del 對象[]  的時候,會執行類內部的__delitem__方法,這個方法有一個參數,參數類型和上邊兩個同樣的。

4.super的使用

#super

class Foo():

    def f1(self):
        print("Foo.f1")


class Bar(Foo):

    def f1(self):
        super(Bar,self).f1()
        print("Bar.f1")


b = Bar()
b.f1()

執行結果以下:

Foo.f1
Bar.f1
代碼解析:super方法用在類繼承中,格式爲super(子類,self).父類的方法()。它的做用是能夠在不修改父類的狀況下,添加本身的功能。下面是個有序字典的例子。
##有序字典,利用super
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):
        temp_list = []
        for i in self.li:
            value = self.get(i)
            temp_list.append("'%s':%s" % (i,value))
        temp_str = "{" + ",".join(temp_list) + "}"
        return temp_str


obj = Mydict()
obj["name"] = "lk"
obj["age"] = 23
obj["salary"] = 3000
print(obj)
print(type(obj))
print(isinstance(obj,dict))

執行結果以下:

{'name':lk,'age':23,'salary':3000}
<class '__main__.Mydict'>
True
代碼解析:定義一個本身的新字典類型Mydict類,繼承字典類;首先要在字典中傳值,調用__init__方法,這個要用到字典類的__init__方法,因此要在Mydict類的__init__方法中寫入super(Mydict,self).__init__(),而咱們想要個有序字典,就是要把傳入的key,存到一個列表中,最後循環列表把相應的value從字典中取到就能夠了。因此新建個空列表要在子類中的__init__方法中執行。而後,傳值的過程當中,執行了對象[] = xxx的形式,這就會調用類內部的__setitem__方法,這個方法也要在子類中定義,不過依然要執行父類字典類的方法,因此要寫上super(Mydict,self).__setitem__(key,value),不過在這裏,咱們能夠把每一個key加入到空列表中去。最後在打印的時候,形式爲print(對象),這個形式就會調用類內部的__str__方法,在子類的__str__方法裏,就能夠根據key,利用字典的get方法,循環打印相應的value值。能夠看到,輸出的結果是Mydict類,也就是dict的子類。這個結果也能夠進行任何字典的操做。

5.設計模式初探之單例模式

概念:單例模式是一種經常使用的軟件設計模式。在它的核心結構中只包含一個被稱爲單例的特殊類。經過單例模式能夠保證系統中一個類只有一個實例。 簡介:單例模式是設計模式中最簡單的形式之一。這一模式的目的是使得類的一個對象成爲系統中的惟一實例。要實現這一點,能夠從客戶端對其進行實例化開始。所以須要用一種只容許生成對象類的惟一實例的機制,「阻止」全部想要生成對象的訪問。使用工廠方法來限制實例化過程。這個方法應該是靜態方法(類方法),由於讓類的實例去生成另外一個惟一實例毫無心義。 動機:對於系統中的某些類來講,只有一個實例很重要,例如,一個系統中能夠存在多個打印任務,可是隻能有一個正在工做的任務;一個系統只能有一個窗口管理器或文件系統;一個系統只能有一個計時工具或ID(序號)生成器。如在Windows中就只能打開一個任務管理器。若是不使用機制對窗口對象進行惟一化,將彈出多個窗口,若是這些窗口顯示的內容徹底一致,則是重複對象,浪費內存資源;若是這些窗口顯示的內容不一致,則意味着在某一瞬間系統有多個狀態,與實際不符,也會給用戶帶來誤解,不知道哪個纔是真實的狀態。所以有時確保系統中某個對象的惟一性即一個類只能有一個實例很是重要。
#設計模式之單例模式

class Foo():

    instance = None

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

    @classmethod
    def get_instance(cls,name):
        if cls.instance:
            return cls.instance
        else:
            obj = cls(name)
            cls.instance = obj
            return obj

obj1 = Foo.get_instance("lk")
obj2 = Foo.get_instance("lk")
print(obj1,obj2)

執行結果以下:

<__main__.Foo object at 0x107704e48> <__main__.Foo object at 0x107704e48>
代碼解析:第一次執行obj1 = Foo.get_instance("lk")的時候,會調用類方法get_instance,經過判斷靜態字段instance = None,會生成一個對象:obj = cls(name),把這個對象給靜態字段instance,返回這個對象。此時建立成功對象obj1。 第二次執行obj2 = Foo.get_instance("lk")的時候,由於靜態字段非空,表明以前建立過對象,因此直接返回第一次建立的對象。 再次輸出的時候,能夠看到兩次對象的內存地址是相同的,這就是單例模式的在Python中的簡單實現。
相關文章
相關標籤/搜索