真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

包含python函數(shù)是描述符的詞條

python中函數(shù)定義

1、函數(shù)定義

創(chuàng)新互聯(lián)-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價(jià)比久治網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式久治網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋久治地區(qū)。費(fèi)用合理售后完善,10年實(shí)體公司更值得信賴。

①使用def關(guān)鍵字定義函數(shù)

def 函數(shù)名(參數(shù)1.參數(shù)2.參數(shù)3...):

"""文檔字符串,docstring,用來說明函數(shù)的作用"""

#函數(shù)體

return 表達(dá)式

注釋的作用:說明函數(shù)是做什么的,函數(shù)有什么功能。

③遇到冒號(hào)要縮進(jìn),冒號(hào)后面所有的縮進(jìn)的代碼塊構(gòu)成了函數(shù)體,描述了函數(shù)是做什么的,即函數(shù)的功能是什么。Python函數(shù)的本質(zhì)與數(shù)學(xué)中的函數(shù)的本質(zhì)是一致的。

2、函數(shù)調(diào)用

①函數(shù)必須先定義,才能調(diào)用,否則會(huì)報(bào)錯(cuò)。

②無參數(shù)時(shí)函數(shù)的調(diào)用:函數(shù)名(),有參數(shù)時(shí)函數(shù)的調(diào)用:函數(shù)名(參數(shù)1.參數(shù)2.……)

③不要在定義函數(shù)的時(shí)候在函數(shù)體里面調(diào)用本身,否則會(huì)出不來,陷入循環(huán)調(diào)用。

④函數(shù)需要調(diào)用函數(shù)體才會(huì)被執(zhí)行,單純的只是定義函數(shù)是不會(huì)被執(zhí)行的。

⑤Debug工具中Step into進(jìn)入到調(diào)用的函數(shù)里,Step Into My Code進(jìn)入到調(diào)用的模塊里函數(shù)。

python 描述符(descriptor) 簡(jiǎn)介

① __getattribute__(), 無條件調(diào)用

② 數(shù)據(jù)描述符(data descriptor)

③ 實(shí)例對(duì)象的字典

④ 類的字典

⑤ 非數(shù)據(jù)描述符(non-data descriptor)

⑥ 父類的字典

⑦ __getattr__() 方法

python 函數(shù)是不是描述符

在Python中,訪問一個(gè)屬性的優(yōu)先級(jí)順序按照如下順序:

1.類屬性

2.數(shù)據(jù)描述符

3.實(shí)例屬性

4.非數(shù)據(jù)描述符

5.__getattr__()方法。這個(gè)方法的完整定義如下所示:

[python] view plain copy

def __getattr__(self,attr) :#attr是self的一個(gè)屬性名

pass;

先來闡述下什么叫數(shù)據(jù)描述符。

數(shù)據(jù)描述符是指實(shí)現(xiàn)了__get__,__set__,__del__方法的類屬性(由于Python中,一切皆是對(duì)象,所以你不妨把所有的屬性也看成是對(duì)象)

PS:個(gè)人覺得這里最好把數(shù)據(jù)描述符等效于定義了__get__,__set__,__del__三個(gè)方法的接口。

闡述下這三個(gè)方法:

__get__的標(biāo)準(zhǔn)定義是__get__(self,obj,type=None),它非常接近于JavaBean的get

第一個(gè)函數(shù)是調(diào)用它的實(shí)例,obj是指去訪問屬性所在的方法,最后一個(gè)type是一個(gè)可選參數(shù),通常為None(這個(gè)有待于進(jìn)一步的研究)

例如給定類X和實(shí)例x,調(diào)用x.foo,等效于調(diào)用:

type(x).__dict__["foo"].__get__(x,type(x))

調(diào)用X.foo,等效于調(diào)用:

type(x).__dict__["foo"].__get__(None,type(x))

第二個(gè)函數(shù)__set__的標(biāo)準(zhǔn)定義是__set__(self,obj,val),它非常接近于JavaBean的set方法,其中最后一個(gè)參數(shù)是要賦予的值

第三個(gè)函數(shù)__del__的標(biāo)準(zhǔn)定義是__del__(self,obj),它非常接近Java中Object的Finailize()方法,指

Python在回收這個(gè)垃圾對(duì)象時(shí)所調(diào)用到的析構(gòu)函數(shù),只是這個(gè)函數(shù)永遠(yuǎn)不會(huì)拋出異常。因?yàn)檫@個(gè)對(duì)象已經(jīng)沒有引用指向它,拋出異常沒有任何意義。

接下來,我們來一一比較這些優(yōu)先級(jí).

首先來看類屬性

[python] view plain copy

# -*- coding:utf-8 -*-

'''''

Created on 2013-3-29

@author: naughty

'''

class A(object):

foo=3

print A.foo

a=A()

print a.foo

a.foo=4

print a.foo

print A.foo

上面這段代碼的輸出如下:

3

3

4

3

從輸出可以看到,當(dāng)我們給a.foo賦值的時(shí)候,其實(shí)與類實(shí)例的那個(gè)foo是沒有關(guān)系的。a.foo=4 這句話給a對(duì)象增加了一個(gè)屬性叫foo。其值是4。

最后兩個(gè)語句明確的表明了,我們輸出a.foo和A.foo的值,他們是不同的。

但是為什么a=A()語句后面的print

a.foo輸出了3呢?這是因?yàn)楦鶕?jù)搜索順序找到了類屬性。當(dāng)我們執(zhí)行a.foo=4的時(shí)候,我們讓a對(duì)象的foo屬性指向了4這個(gè)對(duì)象。但是并沒有改變

類屬性foo的值。所以最后我們print A.foo的時(shí)候,又輸出了3。

[python] view plain copy

# -*- coding:utf-8 -*-

'''''

Created on 2013-3-29

@author: naughty

'''

class A(object):

foo=3

a=A()

a.foo=4

print a.foo

del a.foo

print a.foo

上面的代碼,我給a.foo賦值為4,在輸出一次之后就del了。兩次輸出,第一次輸出的是a對(duì)象的屬性。第二次是類屬性。不是說類屬性的優(yōu)先級(jí)比

實(shí)例屬性的高嗎。為啥第一次輸出的是4而不是3呢?還是上面解釋的原因。因?yàn)閍.foo與類屬性的foo只是重名而已。我們print

a.foo的時(shí)候,a的foo指向的是4,所以輸出了4。

------------------------------------

然后我們來看下數(shù)據(jù)描述符這一全新的語言概念。按照之前的定義,一個(gè)實(shí)現(xiàn)了__get__,__set__,__del__的類都統(tǒng)稱為數(shù)據(jù)描述符。我們來看下一個(gè)簡(jiǎn)單的例子。

[python] view plain copy

# -*- coding:utf-8 -*-

'''''

Created on 2013-3-29

@author: naughty

'''

class simpleDescriptor(object):

def __get__(self,obj,type=None):

pass

def __set__(self,obj,val):

pass

def __del__(self,obj):

pass

class A(object):

foo=simpleDescriptor()

print str(A.__dict__)

print A.foo

a=A()

print a.foo

a.foo=13

print a.foo

上面例子的輸出結(jié)果如下:

[plain] view plain copy

{'__dict__': attribute '__dict__' of 'A' objects, '__module__': '__main__', 'foo': __main__.simpleDescriptor object at 0x005511B0, '__weakref__': attribute '__weakref__' of 'A' objects, '__doc__': None}

None

None

None

從輸出結(jié)果看出,print語句打印出來的都是None。這說明a.foo都沒有被賦值內(nèi)容。這是因?yàn)開_get__函數(shù)的函數(shù)體什么工作都沒有做。直接是pass。此時(shí),想要訪問foo,每次都沒有返回內(nèi)容,所以輸出的內(nèi)容就是None了。

[python] view plain copy

# -*- coding:utf-8 -*-

'''''

Created on 2013-3-29

@author: naughty

'''

class simpleDescriptor(object):

def __get__(self,obj,type=None):

return "hi there"

def __set__(self,obj,val):

pass

def __del__(self,obj):

pass

class A(object):

foo=simpleDescriptor()

print str(A.__dict__)

print A.foo

a=A()

print a.foo

a.foo=13

print a.foo

把__get__函數(shù)實(shí)現(xiàn)以下,就可以得到如下輸出結(jié)果:

[plain] view plain copy

{'__dict__': attribute '__dict__' of 'A' objects, '__module__': '__main__', 'foo': __main__.simpleDescriptor object at 0x00671190, '__weakref__': attribute '__weakref__' of 'A' objects, '__doc__': None}

hi there

hi there

hi there

為了加深對(duì)數(shù)據(jù)描述符的理解,看如下例子:

[python] view plain copy

# -*- coding:utf-8 -*-

'''''

Created on 2013-3-29

@author: naughty

'''

class simpleDescriptor(object):

def __init__(self):

self.result = None;

def __get__(self, obj, type=None) :

return self.result - 10;

def __set__(self, obj, val):

self.result = val + 3;

print self.result;

def __del__(self, obj):

pass

class A(object):

foo = simpleDescriptor();

a = A();

a.foo = 13;

print a.foo;

上面代碼的輸出是

16

6

第一個(gè)16為我們?cè)趯?duì)a.foo賦值的時(shí)候,人為的將13加上3后作為foo的值,第二個(gè)6是我們?cè)诜祷豠.foo之前人為的將它減去了10。

所以我們可以猜測(cè),常規(guī)的Python類在定義get,set方法的時(shí)候,如果無特殊需求,直接給對(duì)應(yīng)的屬性賦值或直接返回該屬性值。如果自己定義類,并且繼承object類的話,這幾個(gè)方法都不用定義。

-----------------

在這里看一個(gè)題外話。

看代碼

[python] view plain copy

# -*- coding:utf-8 -*-

'''''

Created on 2013-3-29

@author: naughty

'''

class simpleDescriptor(object):

def __init__(self):

self.result = None;

def __get__(self, obj, type=None) :

return self.result - 10;

def __set__(self, obj, val):

if isinstance(val,str):

assert False,"int needed! but get str"

self.result = val + 3;

print self.result;

def __del__(self, obj):

pass

class A(object):

foo = simpleDescriptor();

a = A();

a.foo = "13";

print a.foo;

上面代碼在__set__ 函數(shù)中檢查了參數(shù)val,如果val是str類型的,那么要報(bào)錯(cuò)。這就實(shí)現(xiàn)了我們上一篇文章中要實(shí)現(xiàn)的,在給屬性賦值的時(shí)候做類型檢查的功能。

-----------------------------------------------

下面我們來看下實(shí)例屬性和非數(shù)據(jù)描述符。

[python] view plain copy

# -*- coding:utf-8 -*-

'''''

Created on 2013-3-29

@author: naughty

'''

class B(object):

foo = 1.3

b = B()

print b.__dict__

b.bar = 13

print b.__dict__

print b.bar

上面代碼輸出結(jié)果如下:

{}

{'bar': 13}

13

那么什么是非數(shù)據(jù)描述符呢?

簡(jiǎn)單的說,就是沒有實(shí)現(xiàn)get,set,del三個(gè)方法的所有類。

讓我們?nèi)我饪匆粋€(gè)函數(shù)的描述:

def call():

pass

執(zhí)行print dir(call)會(huì)得到如下結(jié)果:

[plain] view plain copy

['__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__doc__', '__format__', '__get__', '__getattribute__', '__globals__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name']

先看下dir的幫助。

dir列出給定對(duì)象的屬性或者是從這個(gè)對(duì)象能夠達(dá)到的對(duì)象。

回到print dir(call)方法的輸出,看到,call方法,有輸出的那么多個(gè)屬性。其中就包含了__get__函數(shù)。但是卻沒有__set__和__del__函數(shù)。所以所有的類成員函數(shù)都是非數(shù)據(jù)描述符。

看一個(gè)實(shí)例數(shù)據(jù)掩蓋非數(shù)據(jù)描述符的例子:

[python] view plain copy

'''''

Created on 2013-3-29

@author: naughty

'''

class simpleDescriptor(object):

def __get__(self,obj,type=None) :

return "get",self,obj,type

class D(object):

foo=simpleDescriptor()

d=D()

print d.foo

d.foo=15

print d.foo

看輸出:

('get', __main__.simpleDescriptor object at 0x02141190,

__main__.D object at 0x025CAF50, class '__main__.D')

15

可見,實(shí)例數(shù)據(jù)掩蓋了非數(shù)據(jù)描述符。

如果改成數(shù)據(jù)描述符,那么就不會(huì)被覆蓋了??聪旅妫?/p>

[python] view plain copy

'''''

Created on 2013-3-29

@author: naughty

'''

class simpleDescriptor(object):

def __get__(self,obj,type=None) :

return "get",self,obj,type

def __set__(self,obj,type=None) :

pass

def __del__(self,obj,type=None) :

pass

class D(object):

foo=simpleDescriptor()

d=D()

print d.foo

d.foo=15

print d.foo

代碼的輸出如下:

[plain] view plain copy

('get', __main__.simpleDescriptor object at 0x01DD1190, __main__.D object at 0x0257AF50, class '__main__.D')

('get', __main__.simpleDescriptor object at 0x01DD1190, __main__.D object at 0x0257AF50, class '__main__.D')

由于是數(shù)據(jù)描述符,__set __函數(shù)體是pass,所以兩次輸出都是同樣的內(nèi)容。

最后看下__getatrr__方法。它的標(biāo)準(zhǔn)定義是:__getattr__(self,attr),其中attr是屬性名

如何正確地使用Python的屬性和描述符

關(guān)于@property裝飾器

在Python中我們使用@property裝飾器來把對(duì)函數(shù)的調(diào)用偽裝成對(duì)屬性的訪問。

那么為什么要這樣做呢?因?yàn)锧property讓我們將自定義的代碼同變量的訪問/設(shè)定聯(lián)系在了一起,同時(shí)為你的類保持一個(gè)簡(jiǎn)單的訪問屬性的接口。

舉個(gè)栗子,假如我們有一個(gè)需要表示電影的類:

1

2

3

4

5

6

7

8

class Movie(object):

def __init__(self, title, description, score, ticket):

self.title = title

self.description = description

self.score = scroe

self.ticket = ticket

你開始在項(xiàng)目的其他地方使用這個(gè)類,但是之后你意識(shí)到:如果不小心給電影打了負(fù)分怎么辦?你覺得這是錯(cuò)誤的行為,希望Movie類可以阻止這個(gè)錯(cuò)誤。 你首先想到的辦法是將Movie類修改為這樣:

Python

1

2

3

4

5

6

7

8

class Movie(object):

def __init__(self, title, description, score, ticket):

self.title = title

self.description = description

self.ticket = ticket

if score 0:

raise ValueError("Negative value not allowed:{}".format(score))

self.score = scroe

但這行不通。因?yàn)槠渌糠值拇a都是直接通過Movie.score來賦值的。這個(gè)新修改的類只會(huì)在__init__方法中捕獲錯(cuò)誤的數(shù)據(jù),但對(duì)于已經(jīng)存在的類實(shí)例就無能為力了。如果有人試著運(yùn)行m.scrore= -100,那么誰也沒法阻止。那該怎么辦?

Python的property解決了這個(gè)問題。

我們可以這樣做

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

class Movie(object):

def __init__(self, title, description, score):

self.title = title

self.description = description

self.score = score

self.ticket = ticket

@property

def score(self):

return self.__score

@score.setter

def score(self, score):

if score 0:

raise ValueError("Negative value not allowed:{}".format(score))

self.__score = score

@score.deleter

def score(self):

raise AttributeError("Can not delete score")

這樣在任何地方修改score都會(huì)檢測(cè)它是否小于0。

property的不足

對(duì)property來說,最大的缺點(diǎn)就是它們不能重復(fù)使用。舉個(gè)例子,假設(shè)你想為ticket字段也添加非負(fù)檢查。下面是修改過的新類:

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

class Movie(object):

def __init__(self, title, description, score, ticket):

self.title = title

self.description = description

self.score = score

self.ticket = ticket

@property

def score(self):

return self.__score

@score.setter

def score(self, score):

if score 0:

raise ValueError("Negative value not allowed:{}".format(score))

self.__score = score

@score.deleter

def score(self):

raise AttributeError("Can not delete score")

@property

def ticket(self):

return self.__ticket

@ticket.setter

def ticket(self, ticket):

if ticket 0:

raise ValueError("Negative value not allowed:{}".format(ticket))

self.__ticket = ticket

@ticket.deleter

def ticket(self):

raise AttributeError("Can not delete ticket")

可以看到代碼增加了不少,但重復(fù)的邏輯也出現(xiàn)了不少。雖然property可以讓類從外部看起來接口整潔漂亮,但是卻做不到內(nèi)部同樣整潔漂亮。

描述符登場(chǎng)

什么是描述符?

一般來說,描述符是一個(gè)具有綁定行為的對(duì)象屬性,其屬性的訪問被描述符協(xié)議方法覆寫。這些方法是__get__()、__set__()和__delete__(),一個(gè)對(duì)象中只要包含了這三個(gè)方法中的至少一個(gè)就稱它為描述符。

描述符有什么作用?

The default behavior for attribute access is to get, set, or delete the attribute from an object’s dictionary. For instance, a.x has a lookup chain starting witha.__dict__[‘x’], then type(a).__dict__[‘x’], and continuing through the base classes of type(a) excluding metaclasses. If the looked-up value is an object defining one of the descriptor methods, then Python may override the default behavior and invoke the descriptor method instead. Where this occurs in the precedence chain depends on which descriptor methods were defined.—–摘自官方文檔

簡(jiǎn)單的說描述符會(huì)改變一個(gè)屬性的基本的獲取、設(shè)置和刪除方式。

先看如何用描述符來解決上面 property邏輯重復(fù)的問題。

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

class Integer(object):

def __init__(self, name):

self.name = name

def __get__(self, instance, owner):

return instance.__dict__[self.name]

def __set__(self, instance, value):

if value 0:

raise ValueError("Negative value not allowed")

instance.__dict__[self.name] = value

class Movie(object):

score = Integer('score')

ticket = Integer('ticket')

因?yàn)槊枋龇麅?yōu)先級(jí)高并且會(huì)改變默認(rèn)的get、set行為,這樣一來,當(dāng)我們?cè)L問或者設(shè)置Movie().score的時(shí)候都會(huì)受到描述符Integer的限制。

不過我們也總不能用下面這樣的方式來創(chuàng)建實(shí)例。

a = Movie()

a.score = 1

a.ticket = 2

a.title = ‘test’

a.descript = ‘…’

這樣太生硬了,所以我們還缺一個(gè)構(gòu)造函數(shù)。

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

class Integer(object):

def __init__(self, name):

self.name = name

def __get__(self, instance, owner):

if instance is None:

return self

return instance.__dict__[self.name]

def __set__(self, instance, value):

if value 0:

raise ValueError('Negative value not allowed')

instance.__dict__[self.name] = value

class Movie(object):

score = Integer('score')

ticket = Integer('ticket')

def __init__(self, title, description, score, ticket):

self.title = title

self.description = description

self.score = score

self.ticket = ticket

這樣在獲取、設(shè)置和刪除score和ticket的時(shí)候都會(huì)進(jìn)入Integer的__get__、__set__,從而減少了重復(fù)的邏輯。

現(xiàn)在雖然問題得到了解決,但是你可能會(huì)好奇這個(gè)描述符到底是如何工作的。具體來說,在__init__函數(shù)里訪問的是自己的self.score和self.ticket,怎么和類屬性score和ticket關(guān)聯(lián)起來的?

描述符如何工作

看官方的說明

If an object defines both __get__() and __set__(), it is considered a data descriptor. Descriptors that only define __get__() are called non-data descriptors (they are typically used for methods but other uses are possible).

Data and non-data descriptors differ in how overrides are calculated with respect to entries in an instance’s dictionary. If an instance’s dictionary has an entry with the same name as a data descriptor, the data descriptor takes precedence. If an instance’s dictionary has an entry with the same name as a non-data descriptor, the dictionary entry takes precedence.

The important points to remember are:

descriptors are invoked by the __getattribute__() method

overriding __getattribute__() prevents automatic descriptor calls

object.__getattribute__() and type.__getattribute__() make different calls to __get__().

data descriptors always override instance dictionaries.

non-data descriptors may be overridden by instance dictionaries.

類調(diào)用__getattribute__()的時(shí)候大概是下面這樣子:

1

2

3

4

5

6

7

def __getattribute__(self, key):

"Emulate type_getattro() in Objects/typeobject.c"

v = object.__getattribute__(self, key)

if hasattr(v, '__get__'):

return v.__get__(None, self)

return v

下面是摘自國(guó)外一篇博客上的內(nèi)容。

Given a Class “C” and an Instance “c” where “c = C(…)”, calling “c.name” means looking up an Attribute “name” on the Instance “c” like this:

Get the Class from Instance

Call the Class’s special method getattribute__. All objects have a default __getattribute

Inside getattribute

Get the Class’s mro as ClassParents

For each ClassParent in ClassParents

If the Attribute is in the ClassParent’s dict

If is a data descriptor

Return the result from calling the data descriptor’s special method __get__()

Break the for each (do not continue searching the same Attribute any further)

If the Attribute is in Instance’s dict

Return the value as it is (even if the value is a data descriptor)

For each ClassParent in ClassParents

If the Attribute is in the ClassParent’s dict

If is a non-data descriptor

Return the result from calling the non-data descriptor’s special method __get__()

If it is NOT a descriptor

Return the value

If Class has the special method getattr

Return the result from calling the Class’s special method__getattr__.

我對(duì)上面的理解是,訪問一個(gè)實(shí)例的屬性的時(shí)候是先遍歷它和它的父類,尋找它們的__dict__里是否有同名的data descriptor如果有,就用這個(gè)data descriptor代理該屬性,如果沒有再尋找該實(shí)例自身的__dict__,如果有就返回。任然沒有再查找它和它父類里的non-data descriptor,最后查找是否有__getattr__

描述符的應(yīng)用場(chǎng)景

python的property、classmethod修飾器本身也是一個(gè)描述符,甚至普通的函數(shù)也是描述符(non-data discriptor)

django model和SQLAlchemy里也有描述符的應(yīng)用

Python

1

2

3

4

5

6

7

8

9

10

11

12

class User(db.Model):

id = db.Column(db.Integer, primary_key=True)

username = db.Column(db.String(80), unique=True)

email = db.Column(db.String(120), unique=True)

def __init__(self, username, email):

self.username = username

self.email = email

def __repr__(self):

return 'User %r' % self.username

后記

只有當(dāng)確實(shí)需要在訪問屬性的時(shí)候完成一些額外的處理任務(wù)時(shí),才應(yīng)該使用property。不然代碼反而會(huì)變得更加啰嗦,而且這樣會(huì)讓程序變慢很多。


當(dāng)前標(biāo)題:包含python函數(shù)是描述符的詞條
標(biāo)題來源:http://weahome.cn/article/dogocii.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部