Python面向?qū)ο缶幊倘炕A(chǔ)集合

轉(zhuǎn)自:[http://www.langzi.fun/Python面向?qū)ο缶幊?html]

面向?qū)ο缶幊毯秃瘮?shù)式編程(面向過程編程)都是程序設(shè)計(jì)的方法睛低,不過稍有區(qū)別服傍。

面向過程編程:

1\. 導(dǎo)入各種外部庫
2\. 設(shè)計(jì)各種全局變量
3\. 寫一個函數(shù)完成某個功能
4\. 寫一個函數(shù)完成某個功能
5\. 寫一個函數(shù)完成某個功能
6\. 寫一個函數(shù)完成某個功能
7\. 寫一個函數(shù)完成某個功能
8\. ......
9\. 寫一個main函數(shù)作為程序入口

在多函數(shù)程序中,許多重要的數(shù)據(jù)被放置在全局?jǐn)?shù)據(jù)區(qū)罩抗,這樣它們可以被所有的函數(shù)訪問瘪校。每個函數(shù)都可以具有它們自己的局部數(shù)據(jù)名段,將某些功能代碼封裝到函數(shù)中泣懊,日后便無需重復(fù)編寫,僅調(diào)用函數(shù)即可信夫。從代碼的組織形式來看就是根據(jù)業(yè)務(wù)邏輯從上到下壘代碼 卡啰。

面向?qū)ο缶幊蹋?/p>

1\. 導(dǎo)入各種外部庫
2\. 設(shè)計(jì)各種全局變量
3\. 決定你要的類
4\. 給每個類提供完整的一組操作
5\. 明確地使用繼承來表現(xiàn)不同類之間的共同點(diǎn)
6\. 根據(jù)需要,決定是否寫一個main函數(shù)作為程序入口

面向?qū)ο缶幊讨姓裢澹瑢⒑瘮?shù)和變量進(jìn)一步封裝成類亡脸,類才是程序的基本元素,它將數(shù)據(jù)和操作緊密地連結(jié)在一起大州,并保護(hù)數(shù)據(jù)不會被外界的函數(shù)意外地改變垂谢。類和和類的實(shí)例(也稱對象)是面向?qū)ο蟮暮诵母拍睿呛兔嫦蜻^程編程根暑、函數(shù)式編程的根本區(qū)別徙邻。

并不是非要用面向?qū)ο缶幊蹋茨愕某绦蛟趺丛O(shè)計(jì)方便,但是就目前來說躯畴,基本上都是在使用面向?qū)ο缶幊獭?/p>

類的基本用法

面向?qū)ο笫峭ㄟ^定義class類來定義蓬抄,這么說面向?qū)ο缶幊叹褪侵皇褂胏lass類,在class類中有封裝嚷缭,繼承的功能,并且還可以構(gòu)造要傳入的參數(shù)路幸,方便控制。

案例一

import sys
import time
reload(sys)
sys.setdefaultencoding('utf-8')

class studetn:
    # 定義一個類名為studetn
    def __init__(self,idx):
    # 定義初始化構(gòu)造晃听,這里使用init砰识,還有別的屬性比如reversed辫狼,iter之類的
        self.idx=idx
        # 初始化變量,方便繼承
    def runx(self):
    # 定義運(yùn)行函數(shù)膨处,從上面繼承變量
        print self.idx
        # 打印出idx的值,或者做一些別的處理
        time.sleep(1)
a=studetn('a')
a.runx()
# 這是類的調(diào)用秦叛,一定要記得類的使用方法瀑粥,首先傳入?yún)?shù),類賦值給一個變量a
# 然后調(diào)用這個類下面定義的函數(shù)

一些專業(yè)術(shù)語概念避咆,既然有面向?qū)ο缶幊踢@個高大上的定義了修噪,自然要搭配一些高大上的概念。

  1. 類(Class): 用來描述具有相同屬性和方法的對象的集合樊销。它定義了該集合中每個對象所共有的屬性和方法脏款。其中的對象被稱作類的實(shí)例。
  2. 實(shí)例:也稱對象剂府。通過類定義的初始化方法剃盾,賦予具體的值淤袜,成為一個”有血有肉的實(shí)體”衰伯。
  3. 實(shí)例化:創(chuàng)建類的實(shí)例的過程或操作。
  4. 實(shí)例變量:定義在實(shí)例中的變量嚎研,只作用于當(dāng)前實(shí)例蓖墅。
  5. 類變量:類變量是所有實(shí)例公有的變量。類變量定義在類中临扮,但在方法體之外论矾。
  6. 數(shù)據(jù)成員:類變量、實(shí)例變量杆勇、方法贪壳、類方法、靜態(tài)方法和屬性等的統(tǒng)稱蚜退。
  7. 方法:類中定義的函數(shù)闰靴。
  8. 靜態(tài)方法:不需要實(shí)例化就可以由類執(zhí)行的方法
  9. 類方法:類方法是將類本身作為對象進(jìn)行操作的方法。
  10. 方法重寫:如果從父類繼承的方法不能滿足子類的需求钻注,可以對父類的方法進(jìn)行改寫,這個過程也稱override幅恋。
  11. 封裝:將內(nèi)部實(shí)現(xiàn)包裹起來杏死,對外透明,提供api接口進(jìn)行調(diào)用的機(jī)制
  12. 繼承:即一個派生類(derived class)繼承父類(base class)的變量和方法捆交。
  13. 多態(tài):根據(jù)對象類型的不同以不同的方式進(jìn)行處理淑翼。

類與實(shí)例

# -*- coding: utf-8 -*-
# @Time    : 2018/5/3 0003 17:02
# @Author  : Langzi
# @Blog    : www.langzi.fun
# @File    : 面向?qū)ο?.py
# @Software: PyCharm
import sys
import time
import requests
reload(sys)
sys.setdefaultencoding('utf-8')

class cc:
    ccc = 'ccc'
    # cc就是類名 如果想要繼承別的類 就class cc(threading) 意思就是從threading繼承
    def __init__(self,a,b,c):
        self.a=a
        self.b=b
        self.c=c
        # 定義構(gòu)造的過程就是實(shí)例化
    def runx(self):
        print self.a*10
        print self.b*5
        print self.c*2
    def runy(self):
        print requests.get('http://www.langzi.fun').headers
e = cc('AAA','CCC','EEE')
e.runx()
e.runy()
# 這兩個就是調(diào)用類里面的方法
print e.c
#實(shí)例變量指的是實(shí)例本身擁有的變量。每個實(shí)例的變量在內(nèi)存中都不一樣品追。
print e.ccc
#類變量玄括,在類里面找到定義的變量。

調(diào)用類的三種方法

實(shí)例方法

# -*- coding: utf-8 -*-
# @Time    : 2018/5/3 0003 17:16
# @Author  : Langzi
# @Blog    : www.langzi.fun
# @File    : 面向?qū)ο?.py
# @Software: PyCharm
import sys
import time
import requests
reload(sys)
sys.setdefaultencoding('utf-8')

class dd:
    def __init__(self,url):
        self.url=url
    def runx(self):
        print requests.get(self.url).status_code

a = dd('http://www.langzi.fun')
a.runx()
# 這種調(diào)用方法就是實(shí)例方法

靜態(tài)方法

靜態(tài)方法由類調(diào)用肉瓦,無默認(rèn)參數(shù)遭京。將實(shí)例方法參數(shù)中的self去掉,然后在方法定義上方加上@staticmethod泞莉,就成為靜態(tài)方法洁墙。它屬于類,和實(shí)例無關(guān)戒财。建議只使用類名.靜態(tài)方法的調(diào)用方式。(雖然也可以使用實(shí)例名.靜態(tài)方法的方式調(diào)用)

# -*- coding: utf-8 -*-
# @Time    : 2018/5/3 0003 17:21
# @Author  : Langzi
# @Blog    : www.langzi.fun
# @File    : 面向?qū)ο?.py
# @Software: PyCharm
import sys
import requests
reload(sys)
sys.setdefaultencoding('utf-8')
class ff:
    @staticmethod
    def runx():
        print requests.get('http://www.langzi.fun').status_code
ff.runx()
#這里就直接調(diào)用了類的變量捺弦,只在類中運(yùn)行而不在實(shí)例中運(yùn)行的方法

經(jīng)常有一些跟類有關(guān)系的功能但在運(yùn)行時又不需要實(shí)例和類參與的情況下需要用到靜態(tài)方法. 比如更改環(huán)境變量或者修改其他類的屬性等能用到靜態(tài)方法. 這種情況可以直接用函數(shù)解決, 但這樣同樣會擴(kuò)散類內(nèi)部的代碼饮寞,造成維護(hù)困難孝扛。

類方法

類方法由類調(diào)用,采用@classmethod裝飾幽崩,至少傳入一個cls(代指類本身苦始,類似self)參數(shù)。執(zhí)行類方法時慌申,自動將調(diào)用該方法的類賦值給cls陌选。建議只使用類名.類方法的調(diào)用方式。(雖然也可以使用實(shí)例名.類方法的方式調(diào)用)

實(shí)際案例

如果要構(gòu)造一個類蹄溉,接受一個網(wǎng)站和這個網(wǎng)站的狀態(tài)碼咨油,然后打印出來。就像這樣:

import sys
import requests
reload(sys)
sys.setdefaultencoding('utf-8')
class gg:
    def __init__(self,url,stat):
        self.url=url
        self.stat=stat
    def outer(self):
        print self.url
        print self.stat
a = gg('langzi',200)
a.outer()

這樣就是使用實(shí)例方法柒爵,雖然可以實(shí)現(xiàn)役电,但是有的時候傳入的參數(shù)并不是(‘langzi’,200)這樣的格式,而是(‘langzi-200’)這樣的棉胀,那該怎么做法瑟?首先要把這個拆分,但是要使用實(shí)例方法實(shí)現(xiàn)起來很麻煩唁奢,這個時候就可以使用類方法霎挟。

# -*- coding: utf-8 -*-
# @Time    : 2018/5/3 0003 17:27
# @Author  : Langzi
# @Blog    : www.langzi.fun
# @File    : 面向?qū)ο?.py
# @Software: PyCharm
import sys
import requests
reload(sys)
sys.setdefaultencoding('utf-8')
class gg:
    url = 0
    stat = 0
    # 因?yàn)槭褂胏lassmethod后會傳入新的變量,所以一開始是需要自己先定義類變量
    def __init__(self,url=0,stat=0):
    # 這里按照正常的定義構(gòu)造函數(shù)
        self.url=url
        self.stat=stat
    @classmethod
    # 裝飾器麻掸,立馬執(zhí)行下面的函數(shù)
    def split(cls,info):
        # 這個函數(shù)接受兩個參數(shù)酥夭,默認(rèn)的cls就是這個類的init函數(shù),info就是外面?zhèn)魅脒M(jìn)來的
        url,stat=map(str,info.split('-'))
        # 這里轉(zhuǎn)換成了格式化的結(jié)構(gòu)
        data = cls(url,stat)
        # 然后執(zhí)行這個類第一個方法论笔,這個類構(gòu)造函數(shù)需要傳入兩個參數(shù)采郎,于是就傳入了兩個參數(shù)
        return data
        # 這里就直接返回了函數(shù)結(jié)果
    def outer(self):
        print self.url
        print self.stat

r = gg.split(('langzi-200'))
r.outer()
# 這里是調(diào)用類方法,與調(diào)用實(shí)例方法一樣

類的特性

封裝

封裝是指將數(shù)據(jù)與具體操作的實(shí)現(xiàn)代碼放在某個對象內(nèi)部狂魔,外部無法訪問蒜埋。必須要先調(diào)用類的方法才能啟動。

案例

class cc:
    ccc = 'ccc'
    # cc就是類名 如果想要繼承別的類 就class cc(threading) 意思就是從threading繼承
    def __init__(self,a,b,c):
        self.a=a
        self.b=b
        self.c=c
print e.ccc
#類變量最楷,在類里面找到定義的變量整份。
print ccc
# 這里會報(bào)錯,這就是封裝籽孙。類中的函數(shù)同理烈评。

繼承

當(dāng)我們定義一個class的時候,可以從某個現(xiàn)有的class繼承犯建,新的class稱為子類(Subclass)讲冠,而被繼承的class稱為基類、父類或超類(Base class适瓦、Super class)竿开。
比如谱仪,我們已經(jīng)編寫了一個名為Animal的class,有一個run()方法可以直接打臃癫省:

class Animal(object):
    def run(self):
        print 'Animal is running...'

當(dāng)我們需要編寫Dog和Cat類時疯攒,就可以直接從Animal類繼承:

class Dog(Animal):
    pass
class Cat(Animal):
    pass

繼承有什么好處?最大的好處是子類獲得了父類的全部功能列荔。由于Animial實(shí)現(xiàn)了run()方法敬尺,因此,Dog和Cat作為它的子類贴浙,什么事也沒干砂吞,就自動擁有了run()方法:

dog = Dog()
dog.run()
cat = Cat()
cat.run()

當(dāng)子類和父類都存在相同的run()方法時,我們說悬而,子類的run()覆蓋了父類的run()呜舒,在代碼運(yùn)行的時候,總是會調(diào)用子類的run()笨奠。這樣袭蝗,我們就獲得了繼承的另一個好處:多態(tài)。

多態(tài)

要理解多態(tài)的好處般婆,我們還需要再編寫一個函數(shù)到腥,這個函數(shù)接受一個Animal類型的變量:

def run_twice(animal):
    animal.run()
    animal.run()

當(dāng)我們傳入Animal的實(shí)例時,run_twice()就打印出:

run_twice(Animal())
運(yùn)行結(jié)果:
Animal is running...
Animal is running...

當(dāng)我們傳入Dog的實(shí)例時蔚袍,run_twice()就打印出:

run_twice(Dog())
運(yùn)行結(jié)果:
Dog is running...
Dog is running...

當(dāng)我們傳入Cat的實(shí)例時乡范,run_twice()就打印出:

run_twice(Cat())
運(yùn)行結(jié)果:
Cat is running...
Cat is running...

看上去沒啥意思,但是仔細(xì)想想啤咽,現(xiàn)在晋辆,如果我們再定義一個Tortoise類型,也從Animal派生:

class Tortoise(Animal):
    def run(self):
        print 'Tortoise is running slowly...'

當(dāng)我們調(diào)用run_twice()時宇整,傳入Tortoise的實(shí)例:

run_twice(Tortoise())
運(yùn)行結(jié)果:
Tortoise is running slowly...
Tortoise is running slowly...

你會發(fā)現(xiàn)瓶佳,新增一個Animal的子類,不必對run_twice()做任何修改鳞青,實(shí)際上霸饲,任何依賴Animal作為參數(shù)的函數(shù)或者方法都可以不加修改地正常運(yùn)行,原因就在于多態(tài)臂拓。

多態(tài)的好處就是厚脉,當(dāng)我們需要傳入Dog、Cat胶惰、Tortoise……時傻工,我們只需要接收Animal類型就可以了,因?yàn)镈og、Cat中捆、Tortoise……都是Animal類型威鹿,然后,按照Animal類型進(jìn)行操作即可轨香。由于Animal類型有run()方法,因此幼东,傳入的任意類型臂容,只要是Animal類或者子類,就會自動調(diào)用實(shí)際類型的run()方法根蟹,這就是多態(tài)的意思:

對于一個變量脓杉,我們只需要知道它是Animal類型,無需確切地知道它的子類型简逮,就可以放心地調(diào)用run()方法球散,而具體調(diào)用的run()方法是作用在Animal、Dog散庶、Cat還是Tortoise對象上蕉堰,由運(yùn)行時該對象的確切類型決定,這就是多態(tài)真正的威力:調(diào)用方只管調(diào)用悲龟,不管細(xì)節(jié)屋讶,而當(dāng)我們新增一種Animal的子類時,只要確保run()方法編寫正確须教,不用管原來的代碼是如何調(diào)用的皿渗。這就是著名的“開閉”原則:

  • 對擴(kuò)展開放:允許新增Animal子類;
  • 對修改封閉:不需要修改依賴Animal類型的run_twice()等函數(shù)轻腺。

總結(jié):繼承可以把父類的所有功能都直接拿過來乐疆,這樣就不必重零做起,子類只需要新增自己特有的方法贬养,也可以把父類不適合的方法覆蓋重寫挤土;
有了繼承,才能有多態(tài)煤蚌。在調(diào)用類實(shí)例方法的時候耕挨,盡量把變量視作父類類型,這樣尉桩,所有子類類型都可以正常被接收筒占;
舊的方式定義Python類允許不從object類繼承,但這種編程方式已經(jīng)嚴(yán)重不推薦使用蜘犁。任何時候翰苫,如果沒有合適的類可以繼承,就繼承自object類。

魔法方法

在上面有提到除了init之外還有iter,reverse的方法奏窑,這里就詳細(xì)說下除了init初始化還有哪些別的方法导披。

__init__ :      構(gòu)造函數(shù),在生成對象時調(diào)用
__del__ :       析構(gòu)函數(shù)埃唯,釋放對象時使用
__repr__ :      打印撩匕,轉(zhuǎn)換
__setitem__ :   按照索引賦值
__getitem__:    按照索引獲取值
__len__:        獲得長度
__cmp__:        比較運(yùn)算
__call__:       調(diào)用
__add__:        加運(yùn)算
__sub__:        減運(yùn)算
__mul__:        乘運(yùn)算
__div__:        除運(yùn)算
__mod__:        求余運(yùn)算
__pow__:        冪

具體使用

1\. __doc__

說明性文檔和信息。Python自建墨叛,無需自定義止毕。

class Foo:
    """ 描述類信息,可被自動收集 """
    def func(self):
        pass
# 打印類的說明文檔 
print(Foo.__doc__)

2\. __init__()

實(shí)例化方法漠趁,通過類創(chuàng)建實(shí)例時扁凛,自動觸發(fā)執(zhí)行。

class Foo:
    def __init__(self, name):
        self.name = name
        self.age = 18
obj = Foo(jack') # 自動執(zhí)行類中的 __init__ 方法

3\. __module__ 和 __class__

module 表示當(dāng)前操作的對象在屬于哪個模塊闯传。
class 表示當(dāng)前操作的對象屬于哪個類谨朝。
這兩者也是Python內(nèi)建,無需自定義甥绿。

class Foo:
    pass
obj = Foo()
print(obj.__module__)
print(obj.__class__)

運(yùn)行結(jié)果:
main
<class 'main.foo'=""></class>

4\. __del__()

析構(gòu)方法字币,當(dāng)對象在內(nèi)存中被釋放時,自動觸發(fā)此方法妹窖。

注:此方法一般無須自定義纬朝,因?yàn)镻ython自帶內(nèi)存分配和釋放機(jī)制,除非你需要在釋放的時候指定做一些動作骄呼。析構(gòu)函數(shù)的調(diào)用是由解釋器在進(jìn)行垃圾回收時自動觸發(fā)執(zhí)行的共苛。

class Foo:
    def __del__(self):
        print("我被回收了!")

obj = Foo()
del obj

5\. __call__()

如果為一個類編寫了該方法蜓萄,那么在該類的實(shí)例后面加括號隅茎,可會調(diào)用這個方法。

注:構(gòu)造方法的執(zhí)行是由類加括號執(zhí)行的嫉沽,即:對象 = 類名()辟犀,而對于call() 方法,是由對象后加括號觸發(fā)的绸硕,即:對象() 或者 類()()

class Foo:
    def __init__(self):
        pass
    def __call__(self, *args, **kwargs):
        print('__call__')
obj = Foo()     # 執(zhí)行 __init__
obj()       # 執(zhí)行 __call__

可以用Python內(nèi)建的callable()函數(shù)進(jìn)行測試堂竟,判斷一個對象是否可以被執(zhí)行。

callable(Student())

運(yùn)行結(jié)果:

True

6\. __dict__

列出類或?qū)ο笾械乃谐蓡T玻佩!非常重要和有用的一個屬性出嘹,Python自建,無需用戶自己定義咬崔。

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__)
# 獲取 對象obj1 的成員 
obj1 = Province('HeBei',10000)
print(obj1.__dict__)
# 獲取 對象obj2 的成員 
obj2 = Province('HeNan', 3888)
print(obj2.__dict__)

7\. __str__()

如果一個類中定義了str()方法税稼,那么在打印對象時烦秩,默認(rèn)輸出該方法的返回值。這也是一個非常重要的方法郎仆,需要用戶自己定義只祠。

下面的類,沒有定義str()方法扰肌,打印結(jié)果是:<main.Foo object at 0x000000000210A358>

class Foo:
    pass
obj = Foo()
print(obj)
定義了__str__()方法后抛寝,打印結(jié)果是:'jack'。
class Foo:
    def __str__(self):
        return 'jack'
obj = Foo()
print(obj)

8曙旭、__getitem__()墩剖、__setitem__()、__delitem__()

取值夷狰、賦值、刪除這“三劍客”的套路郊霎,在Python中沼头,我們已經(jīng)見過很多次了,比如前面的@property裝飾器书劝。

Python中进倍,標(biāo)識符后面加圓括號,通常代表執(zhí)行或調(diào)用方法的意思购对。而在標(biāo)識符后面加中括號[]猾昆,通常代表取值的意思。Python設(shè)計(jì)了getitem()骡苞、setitem()、delitem()這三個特殊成員,用于執(zhí)行與中括號有關(guān)的動作手素。它們分別表示取值窘拯、賦值、刪除數(shù)據(jù)躲株。

也就是如下的操作:

a = 標(biāo)識符[]∑俊:   執(zhí)行__getitem__方法
標(biāo)識符[] = a  :   執(zhí)行__setitem__方法
del 標(biāo)識符[] :   執(zhí)行__delitem__方法

如果有一個類同時定義了這三個魔法方法霜定,那么這個類的實(shí)例的行為看起來就像一個字典一樣档悠,如下例所示:

class Foo:
    def __getitem__(self, key):
        print('__getitem__',key)
    def __setitem__(self, key, value):
        print('__setitem__',key,value)
    def __delitem__(self, key):
        print('__delitem__',key)
obj = Foo()
result = obj['k1']      # 自動觸發(fā)執(zhí)行 __getitem__
obj['k2'] = 'jack'      # 自動觸發(fā)執(zhí)行 __setitem__
del obj['k1']             # 自動觸發(fā)執(zhí)行 __delitem__

9\. __iter__()

這是迭代器方法!列表望浩、字典辖所、元組之所以可以進(jìn)行for循環(huán),是因?yàn)槠鋬?nèi)部定義了 iter()這個方法曾雕。如果用戶想讓自定義的類的對象可以被迭代奴烙,那么就需要在類中定義這個方法,并且讓該方法的返回值是一個可迭代的對象。當(dāng)在代碼中利用for循環(huán)遍歷對象時切诀,就會調(diào)用類的這個iter()方法揩环。

普通的類:

class Foo:
    pass
obj = Foo()
for i in obj:
    print(i)
# 報(bào)錯:TypeError: 'Foo' object is not iterable<br># 原因是Foo對象不可迭代
添加一個__iter__(),但什么都不返回:
class Foo:
    def __iter__(self):
        pass
obj = Foo()
for i in obj:
    print(i)
# 報(bào)錯:TypeError: iter() returned non-iterator of type 'NoneType'
#原因是 __iter__方法沒有返回一個可迭代的對象

返回一個個迭代對象:

class Foo:
    def __init__(self, sq):
        self.sq = sq
    def __iter__(self):
        return iter(self.sq)
obj = Foo([11,22,33,44])
for i in obj:
    print(i)

最好的方法是使用生成器:

class Foo:
    def __init__(self):
        pass
    def __iter__(self):
        yield 1
        yield 2
        yield 3
obj = Foo()
for i in obj:
    print(i)

10幅虑、__len__()

在Python中丰滑,如果你調(diào)用內(nèi)置的len()函數(shù)試圖獲取一個對象的長度,在后臺倒庵,其實(shí)是去調(diào)用該對象的len()方法褒墨,所以,下面的代碼是等價(jià)的:

len('ABC')
3
'ABC'.__len__()
3

Python的list擎宝、dict郁妈、str等內(nèi)置數(shù)據(jù)類型都實(shí)現(xiàn)了該方法,但是你自定義的類要實(shí)現(xiàn)len方法需要好好設(shè)計(jì)绍申。

11\. __repr__()

這個方法的作用和str()很像噩咪,兩者的區(qū)別是str()返回用戶看到的字符串,而repr()返回程序開發(fā)者看到的字符串极阅,也就是說胃碾,repr()是為調(diào)試服務(wù)的。通常兩者代碼一樣筋搏。

class Foo:
    def __init__(self, name):
        self.name = name
    def __str__(self):
        return "this is %s" % self.name
    __repr__ = __str__

12\. __add__: 加運(yùn)算 __sub__: 減運(yùn)算 __mul__: 乘運(yùn)算 __div__: 除運(yùn)算 __mod__: 求余運(yùn)算 __pow__: 冪運(yùn)算

這些都是算術(shù)運(yùn)算方法仆百,需要你自己為類設(shè)計(jì)具體運(yùn)算代碼。有些Python內(nèi)置數(shù)據(jù)類型奔脐,比如int就帶有這些方法俄周。Python支持運(yùn)算符的重載,也就是重寫髓迎。

class Vector:
   def __init__(self, a, b):
      self.a = a
      self.b = b
   def __str__(self):
      return 'Vector (%d, %d)' % (self.a, self.b)
   def __add__(self,other):
      return Vector(self.a + other.a, self.b + other.b)
v1 = Vector(2,10)
v2 = Vector(5,-2)
print (v1 + v2)

13\. __author__作者信息

__author__ = "Jack"
def show():
    print(__author__)
show()

14\. __slots__

Python作為一種動態(tài)語言栈源,可以在類定義完成和實(shí)例化后,給類或者對象繼續(xù)添加隨意個數(shù)或者任意類型的變量或方法竖般,這是動態(tài)語言的特性甚垦。例如:

def print_doc(self):
    print("haha")

class Foo:
    pass

obj1 = Foo()
obj2 = Foo()
# 動態(tài)添加實(shí)例變量
obj1.name = "jack"
obj2.age = 18
# 動態(tài)的給類添加實(shí)例方法
Foo.show = print_doc
obj1.show()
obj2.show()

但是!如果我想限制實(shí)例可以添加的變量怎么辦涣雕?可以使slots限制實(shí)例的變量艰亮,比如,只允許Foo的實(shí)例添加name和age屬性挣郭。

def print_doc(self):
    print("haha")
class Foo:
    __slots__ = ("name", "age")
    pass
obj1 = Foo()
obj2 = Foo()
# 動態(tài)添加實(shí)例變量
obj1.name = "jack"
obj2.age = 18
obj1.sex = "male"       # 這一句會彈出錯誤
# 但是無法限制給類添加方法
Foo.show = print_doc
obj1.show()
obj2.show()
由于'sex'不在__slots__的列表中迄埃,所以不能綁定sex屬性,試圖綁定sex將得到AttributeError的錯誤兑障。
Traceback (most recent call last):
  File "F:/Python/pycharm/201705/1.py", line 14, in <module>
    obj1.sex = "male"
AttributeError: 'Foo' object has no attribute 'sex'

需要提醒的是侄非,slots定義的屬性僅對當(dāng)前類的實(shí)例起作用蕉汪,對繼承了它的子類是不起作用的。想想也是這個道理逞怨,如果你繼承一個父類者疤,卻莫名其妙發(fā)現(xiàn)有些變量無法定義,那不是大問題么叠赦?如果非要子類也被限制驹马,除非在子類中也定義slots,這樣除秀,子類實(shí)例允許定義的屬性就是自身的slots加上父類的slots糯累。

成員保護(hù)與訪問機(jī)制

有些對象你不想外部訪問,即使是通過調(diào)用類對象也無法訪問册踩,那就請認(rèn)真學(xué)完本章節(jié)泳姐。

私有成員

class obj:
    def __init__(self,name):
        self.name=name
    def pri(self):
        print self.name
    __age = 18
    # 加上雙下劃線的就是私有變量,只能在類的內(nèi)部訪問暂吉,外部無法訪問
a = obj('zhao')
a.pri()

運(yùn)行結(jié)果:

zhao

如果要在類中調(diào)用這個私有成員仗岸,可以這么用

class obj:
    def __init__(self,name):
        self.name=name
    def prin(self):
        print self.name
    __age = 18
    # 加上雙下劃線的就是私有變量,只能在類的內(nèi)部訪問借笙,外部無法訪問
    @classmethod
    # 如果要在類中調(diào)用,首先調(diào)用類方法
    def pri(cls):
        print cls.__age
        # 然后在使用
a = obj('zhao')
a.prin()
obj.pri()
# 通過這樣直接調(diào)用類中的私有變量

運(yùn)行結(jié)果:

zhao
18

使用get-set-del方法操作私有成員

class obj:
    def __init__(self,name):
        self.name=name
    def prin(self):
        print self.name
    __age = 18
    # 加上雙下劃線的就是私有變量较锡,只能在類的內(nèi)部訪問业稼,外部無法訪問
    @classmethod
    # 如果要在類中調(diào)用,首先調(diào)用類方法
    def pri(cls):
        print cls.__age
        # 然后在使用
    @classmethod
    def set_age(cls,value):
        cls.__age = value
        return cls.__age
        # 這個用法就是改變__age的值
    @classmethod
    def get_age(cls):
        return cls.__age
        # 這個用法就是直接返回__age的值
    @classmethod
    def del_age(cls):
        del cls.__age
        # 這個用法就是直接刪除__age的值

print obj.get_age()
# 這里是直接調(diào)用出__age的值  返回值18
print obj.set_age(20)
# 這里是直接改變__age的值  返回值20
obj.del_age()
# 這里是直接刪除__age的值

思考: 既然是私有變量蚂蕴,不讓外部訪問低散,為何有要在后面調(diào)用又改變呢?因?yàn)榭梢詫λ接凶兞窟M(jìn)行額外的檢測骡楼,處理熔号,加工等等。比如判斷value的值鸟整,使用isinstance然后做if-else判斷引镊。

使用私有變量可以對內(nèi)部變量進(jìn)行保護(hù),外部無法改變篮条,但是可以對它進(jìn)行檢測處理弟头。

這里引申一下私有成員的保護(hù)機(jī)制,使用__age對私有變量其實(shí)就是—>obj._obj__age的樣子進(jìn)行保護(hù)涉茧,說白了你直接使用obj._obj__age就可以直接調(diào)用內(nèi)部私有變量age了赴恨。

Propety裝飾器

把類的方法偽裝成屬性調(diào)用的方式,就是把類里面的一個函數(shù)伴栓,變成一個屬性一樣的東西~
一開始調(diào)用類的方法要使用圓括號伦连,現(xiàn)在變成了屬性進(jìn)行讀取設(shè)置存儲雨饺。
舉個例子來說明:

常用的調(diào)用方法

class obj:
    def __init__(self,name,age):
        self.__name=name
        self.__age=age
        # 講這些設(shè)置成私有變量
    def get_age(self):
        return self.__age
    def set_age(self,value):
        if isinstance(value,int):
            self.__age=value
        else:
            raise ValueError('非整數(shù)類型')
    def del_age(self):
        print 'delete over'
a = obj('langzi',18)
print a.get_age()
a.set_age(20)
print a.get_age()

使用裝飾器

class obj:
    def __init__(self,name,age):
        self.__name=name
        self.__age=age
        # 把這些設(shè)置成私有變量
    @property
    def age(self):
        return self.__age
    @age.setter
    def age(self,value):
        if isinstance(value,int):
            self.__age=value
        else:
            raise ValueError('非整數(shù)類型')
    @age.deleter
    def age(self):
        print 'delete over'
a = obj('langzi',18)
# 使用這些裝飾器,可以使用類與對象的方法直接調(diào)用
print a.age
# 這里就是直接調(diào)用返回age的值
a.age=20
# 這里就是直接使用setter把值轉(zhuǎn)換
print a.age
del a.age
# 刪除age

當(dāng)然這種調(diào)用方法有些麻煩惑淳,每次都是一個一個去實(shí)例類與對象额港,有個更加簡單直觀的方法。

更加減半的使用property()函數(shù)

除了使用裝飾器的方式將一個方法偽裝成屬性外汛聚,Python內(nèi)置的builtins模塊中的property()函數(shù)锹安,為我們提供了第二種設(shè)置類屬性的手段。

class People:

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

    def get_age(self):
        return self.__age

    def set_age(self, age):
        if isinstance(age, int):
            self.__age = age
        else:
            raise ValueError

    def del_age(self):
        print("刪除年齡數(shù)據(jù)倚舀!")

    # 核心在這句
    age = property(get_age, set_age, del_age, "年齡")    

obj = People("jack", 18)
print(obj.age)
obj.age = 19
print("obj.age:  ", obj.age)
del obj.ag

通過語句age = property(get_age, set_age, del_age, “年齡”)將一個方法偽裝成為屬性叹哭。其效果和裝飾器的方法是一樣的。

property()函數(shù)的參數(shù):

第一個參數(shù)是方法名痕貌,調(diào)用 實(shí)例.屬性 時自動執(zhí)行的方法
第二個參數(shù)是方法名风罩,調(diào)用 實(shí)例.屬性 = XXX時自動執(zhí)行的方法
第三個參數(shù)是方法名,調(diào)用 del 實(shí)例.屬性 時自動執(zhí)行的方法
第四個參數(shù)是字符串舵稠,調(diào)用 實(shí)例.屬性.__doc__時的描述信息超升。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市哺徊,隨后出現(xiàn)的幾起案子室琢,更是在濱河造成了極大的恐慌,老刑警劉巖落追,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盈滴,死亡現(xiàn)場離奇詭異,居然都是意外死亡轿钠,警方通過查閱死者的電腦和手機(jī)巢钓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來疗垛,“玉大人症汹,你說我怎么就攤上這事〈螅” “怎么了背镇?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長泽裳。 經(jīng)常有香客問我芽世,道長,這世上最難降的妖魔是什么诡壁? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任济瓢,我火速辦了婚禮,結(jié)果婚禮上妹卿,老公的妹妹穿的比我還像新娘旺矾。我一直安慰自己蔑鹦,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布箕宙。 她就那樣靜靜地躺著嚎朽,像睡著了一般。 火紅的嫁衣襯著肌膚如雪柬帕。 梳的紋絲不亂的頭發(fā)上哟忍,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天,我揣著相機(jī)與錄音陷寝,去河邊找鬼锅很。 笑死,一個胖子當(dāng)著我的面吹牛凤跑,可吹牛的內(nèi)容都是我干的爆安。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼仔引,長吁一口氣:“原來是場噩夢啊……” “哼扔仓!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起咖耘,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤翘簇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后儿倒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體版保,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年义桂,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蹈垢。...
    茶點(diǎn)故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡慷吊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出曹抬,到底是詐尸還是另有隱情,我是刑警寧澤谤民,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布堰酿,位于F島的核電站,受9級特大地震影響张足,放射性物質(zhì)發(fā)生泄漏触创。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一为牍、第九天 我趴在偏房一處隱蔽的房頂上張望哼绑。 院中可真熱鬧岩馍,春花似錦、人聲如沸抖韩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽茂浮。三九已至双谆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間席揽,已是汗流浹背顽馋。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留驹尼,地道東北人趣避。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像新翎,于是被迫代替她去往敵國和親程帕。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容