不管你是待業(yè)還是失業(yè),在這個(gè)被互聯(lián)網(wǎng)圍繞的時(shí)代里,選擇python元編程,就多了一項(xiàng)技能,還怕找不到工作?,還怕不好找工作?小編就來(lái)告訴你這個(gè)專業(yè)的優(yōu)勢(shì)到底體現(xiàn)在哪里:Python元編程:控制你想控制的一切,不要輕易使用元編程,學(xué)習(xí)python編程需要安裝哪些軟件???。
1.Python元編程:控制你想控制的一切
很多人不理解“元編程”是個(gè)什么東西,關(guān)于它也沒有一個(gè)十分準(zhǔn)確的定義。這篇文章要說(shuō)的是Python里的元編程,實(shí)際上也不一定就真的符合“元編程”的定義。只不過我無(wú)法找到一個(gè)更準(zhǔn)確的名字來(lái)代表這篇文章的主題,所以就借了這么一個(gè)名號(hào)。副標(biāo)題是控制你想控制的一切,實(shí)際上這篇文章講的都是一個(gè)東西,利用Python提供給我們的特性,盡可能的使代碼優(yōu)雅簡(jiǎn)潔。具體而言,通過編程的方法,在更高的抽象層次上對(duì)一種層次的抽象的特性進(jìn)行修改。首先說(shuō),Python中一切皆對(duì)象,老生常談。還有,Python提供了許多特殊方法、元類等等這樣的“元編程”機(jī)制。像給對(duì)象動(dòng)態(tài)添加屬性方法之類的,在Python中根本談不上是“元編程”,但在某些靜態(tài)語(yǔ)言中卻是需要一定技巧的東西。我們來(lái)談些Python程序員也容易被搞糊涂的東西。我們先來(lái)把對(duì)象分分層次,通常我們知道一個(gè)對(duì)象有它的類型,老早以前Python就將類型也實(shí)現(xiàn)為對(duì)象。這樣我們就有了實(shí)例對(duì)象和類對(duì)象。這是兩個(gè)層次。稍有基礎(chǔ)的讀者就會(huì)知道還有元類這個(gè)東西的存在,簡(jiǎn)言之,元類就是“類”的“類”,也就是比類更高層次的東西。這又有了一個(gè)層次。還有嗎? vs RunTime如果我們換個(gè)角度,不用非得和之前的三個(gè)層次使用同樣的標(biāo)準(zhǔn)。我們?cè)賮?lái)區(qū)分兩個(gè)東西:和RunTime,它們之間也并非界限分明,顧名思義,就是兩個(gè)時(shí)刻,導(dǎo)入時(shí)和運(yùn)行時(shí)。當(dāng)一個(gè)模塊被導(dǎo)入時(shí),會(huì)發(fā)生什么?在全局作用域的語(yǔ)句(非定義性語(yǔ)句)被執(zhí)行。函數(shù)定義呢?一個(gè)函數(shù)對(duì)象被創(chuàng)建,但其中的代碼不會(huì)被執(zhí)行。類定義呢?一個(gè)類對(duì)象被創(chuàng)建,類定義域的代碼被執(zhí)行,類的方法中的代碼自然也不會(huì)被執(zhí)行。執(zhí)行時(shí)呢?函數(shù)和方法中的代碼會(huì)被執(zhí)行。當(dāng)然你要先調(diào)用它們。元類所以我們可以說(shuō)元類和類是屬于的,import一個(gè)模塊之后,它們就會(huì)被創(chuàng)建。實(shí)例對(duì)象屬于RunTime,單import是不會(huì)創(chuàng)建實(shí)例對(duì)象的。不過話不能說(shuō)的太絕對(duì),因?yàn)槿绻阋窃谀K作用域?qū)嵗悾瑢?shí)例對(duì)象也是會(huì)被創(chuàng)建的。只不過我們通常把它們寫在函數(shù)里面,所以這樣劃分。如果你想控制產(chǎn)生的實(shí)例對(duì)象的特性該怎么做?太簡(jiǎn)單了,在類定義中重寫__init__方法。那么我們要控制類的一些性質(zhì)呢?有這種需求嗎?當(dāng)然有!經(jīng)典的單例模式,大家都知道有很多種實(shí)現(xiàn)方式。要求就是,一個(gè)類只能有一個(gè)實(shí)例。最簡(jiǎn)單的實(shí)現(xiàn)方法是這樣的class _Spam: def __init__(self): print("Spam!!!") _spam_singleton =None def Spam(): global _spam_singleton if _spam_singleton is not None: return _spam_singleton else: _spam_singleton = _Spam() return _spam_singleton工廠模式,不太優(yōu)雅。我們?cè)賮?lái)審視一下需求,要一個(gè)類只能有一個(gè)實(shí)例。我們?cè)陬愔卸x的方法都是實(shí)例對(duì)象的行為,那么要想改變類的行為,就需要更高層次的東西。元類在這個(gè)時(shí)候登場(chǎng)在合適不過了。前面說(shuō)過,元類是類的類。也就是說(shuō),元類的__init__方法就是類的初始化方法。 我們知道還有__call__這個(gè)東西,它能讓實(shí)例像函數(shù)那樣被調(diào)用,那么元類的這個(gè)方法就是類在被實(shí)例化時(shí)調(diào)用的方法。代碼就可以寫出來(lái)了:class Singleton(type): def __init__(self, *args, **kwargs): self._instance = None super().__init__(*args, **kwargs) def __call__(self, *args, **kwargs): if self._instance is None: self._instance = super().__call__(*args, **kwargs) return self._instance else: return self._instance class Spam(metaclass=Singleton): def __init__(self): print("Spam!!!")主要有兩個(gè)地方和一般的類定義不同,一是Singleton的基類是type,一是Spam定義的地方有一個(gè)metaclass=Singleton。type是什么?它是object的子類,object是它的實(shí)例。也就是說(shuō),type是所有類的類,也就是最基本的元類,它規(guī)定了一些所有類在產(chǎn)生時(shí)需要的一些操作。所以我們的自定義元類需要子類化type。同時(shí)type也是一個(gè)對(duì)象,所以它又是object的子類。有點(diǎn)不太好理解,大概知道就可以了。裝飾器我們?cè)賮?lái)說(shuō)說(shuō)裝飾器。大多數(shù)人認(rèn)為裝飾器是Python里面最難理解的概念之一。其實(shí)它不過就是一個(gè)語(yǔ)法糖,理解了函數(shù)也是對(duì)象之后。就可以很輕易的寫出自己的裝飾器了。from functools import wraps def print_result(func): @wraps(func) def wrappper(*args, **kwargs): result = func(*args, **kwargs) print(result) return result return wrappper @print_result def add(x, y): return x + y #相當(dāng)于: #add = print_result(add) add(1, 3)這里我們還用到了一個(gè)裝飾器@wraps,它是用來(lái)讓我們返回的內(nèi)部函數(shù)wrapper和原來(lái)的函數(shù)擁有相同的函數(shù)簽名的,基本上我們?cè)趯懷b飾器時(shí)都要加上它。我在注釋里寫了,@decorator這樣的形式等價(jià)于func=decorator(func),理解了這一點(diǎn),我們就可以寫出更多種類的裝飾器。比如類裝飾器,以及將裝飾器寫成一個(gè)類。def attr_upper(cls): for attrname,value in cls.__dict__.items(): if (value,str): if not value.('__'): setattr(cls,attrname,bytes.decode(str.encode(value).upper())) return cls @attr_upper class Person: sex = 'man' print(Person.sex) # MAN注意普通的裝飾器和類裝飾器實(shí)現(xiàn)的不同點(diǎn)。對(duì)數(shù)據(jù)的抽象--描述符如果我們想讓某一些類擁有某些相同的特性,或者說(shuō)可以實(shí)現(xiàn)在類定義對(duì)其的控制,我們可以自定義一個(gè)元類,然后讓它成為這些類的元類。如果我們想讓某一些函數(shù)擁有某些相同的功能,又不想把代碼復(fù)制粘貼一遍,我們可以定義一個(gè)裝飾器。那么,假如我們想讓實(shí)例的屬性擁有某些共同的特點(diǎn)呢?有人可能會(huì)說(shuō)可以用property,當(dāng)然可以。但是這些邏輯必須在每個(gè)類定義的時(shí)候都寫一遍。如果我們想讓這些類的實(shí)例的某些屬性都有相同的特點(diǎn)的話,就可以自定義一個(gè)描述符類。關(guān)于描述符,這篇文章 : def __init__(self, _type): self._type = _type def __get__(self, instance, cls): if instance is None: return self else: return getattr(instance, self.name) def __set_name__(self, cls, name): self.name = name def __set__(self, instance, value): if not (value, self._type): raise TypeError('Expected' + str(self._type)) instance.__dict__[self.name] = value class Person: age = (int) name = (str) def __init__(self, age, name): self.age = age self.name = name jack = Person(15, 'Jack') jack.age = '15' # 會(huì)報(bào)錯(cuò) 在這里面有幾個(gè)角色,是一個(gè)描述符類,Person的屬性是描述符類的實(shí)例,看似描述符是作為Person,也就是類的屬性而不是實(shí)例屬性存在的。但實(shí)際上,一旦Person的實(shí)例訪問了同名的屬性,描述符就會(huì)起作用。需要注意的是,在Python3.5及之前的版本中,是沒有__set_name__這個(gè)特殊方法的,這意味著如果你想要知道在類定義中描述符被起了一個(gè)什么樣的名字,是需要在描述符實(shí)例化時(shí)顯式傳遞給它的,也就是需要多一個(gè)參數(shù)。不過在Python3.6中,這個(gè)問題得到了解決,只需要在描述符類定義中重寫__set_name__這個(gè)方法就好了。還需要注意的是__get__的寫法,基本上對(duì)instance的判斷是必需的,不然會(huì)報(bào)錯(cuò)。原因也不難理解,就不細(xì)說(shuō)了??刂谱宇惖膭?chuàng)建——代替元類的方法在Python3.6中,我們可以通過實(shí)現(xiàn)__init_subclass__特殊方法,來(lái)自定義子類的創(chuàng)建,這樣我們就可以在某些情況下擺脫元類這個(gè)討厭的東西。class : = [] def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) cls..append(cls) class Plugin1(): pass class Plugin2(): pass小結(jié)諸如元類等元編程對(duì)于大多數(shù)人來(lái)說(shuō)有些晦澀難懂,大多數(shù)時(shí)候也無(wú)需用到它們。但是大多數(shù)框架背后的實(shí)現(xiàn)都使用到了這些技巧,這樣才能讓使用者寫出來(lái)的代碼簡(jiǎn)潔易懂。如果你想更深入的了解這些技巧,可以參看一些書籍例如《Fluent Python》、《Python Cookbook》(這篇文章有的內(nèi)容就是參考了它們),或者看官方文檔中的某些章節(jié)例如上文說(shuō)的描述符HowTo,還有Data Model一節(jié)等等?;蛘咧苯涌碢ython的源碼,包括用Python寫的以及CPython的源碼。記住,只有在充分理解了它們之后再去使用,也不要是個(gè)地方就想著使用這些技巧。
2.不要輕易使用元編程
本文轉(zhuǎn)載自: Common Lisp 里的 loop 宏就。一方面,它體現(xiàn)了宏的強(qiáng)大;另一方面,它展現(xiàn)了宏能給我們帶來(lái)的復(fù)雜。熟悉 C/Java 語(yǔ)言都知道循環(huán)是語(yǔ)言本身提供的關(guān)鍵字,一般是 for。但 Lisp 語(yǔ)言特別精簡(jiǎn),它認(rèn)為循環(huán)只是遞歸的一個(gè)特殊形式,語(yǔ)言本身也不包含任何的循環(huán)關(guān)鍵字。于是有人用宏實(shí)現(xiàn)了 loop ,它讓我們能以近乎英語(yǔ)的方式在 Lisp 里寫循環(huán)語(yǔ)句,這里從 這里 摘抄一個(gè)例子:(loop for x in '(a b c d e) for y from 1 if (> y 1) do (format t ", ~A" x) else do (format t "~A" x) )你不需要了解這段代碼的含義,重要的是了解像 for .. in .., if ... do ... else ... do 這樣的語(yǔ)法并不是 Lisp 提供的,而是 loop 宏實(shí)現(xiàn)的,這些語(yǔ)法離開了 loop 也就不再合法。我們看到 loop 宏讓我們能在 Lisp 語(yǔ)言不支持的情況下享受到近乎現(xiàn)代語(yǔ)言中才包含的 for ... in ... 語(yǔ)法。要知道在 Java 中有兩種 for 語(yǔ)句:for (int i=0; i < array.length; i++) { System.out.println("Element: " + array[i]); } for (String element : array) { System.out.println("Element: " + element); }而第二種直到 JDK 1.5 才加入。在這之前,廣大的 Java 程序員即使已經(jīng)認(rèn)識(shí)到了第二種寫法的優(yōu)越性,卻也只能無(wú)奈等到語(yǔ)言支持才行。而 Lisp 程序員很快就能通過宏來(lái)實(shí)現(xiàn)自己理想中的語(yǔ)法。然而光明與黑暗共生,宏給我們帶來(lái)極大自由的同時(shí),也意味著分裂。每個(gè)程序員心中理想的語(yǔ)法各不相同,這就意味著一千個(gè)程序員會(huì)有一千種語(yǔ)法。在 Lisp 中宏是非常容易編寫的(不代表容易正確編寫),意味著真的會(huì)存在一千種語(yǔ)法,大家誰(shuí)也不服誰(shuí),因此造成分裂;但在 C/Java 中,沒有宏的支持,雖然有一千種想法,但大家都寫不出編譯器,于是只能集中討論,統(tǒng)一語(yǔ)法了,再靠大牛們實(shí)現(xiàn)了。而現(xiàn)實(shí)就是如此,Common Lisp 嘗試標(biāo)準(zhǔn)化 Lisp,但依舊有人不認(rèn)同這種理念,例如 Scheme,Common Lisp 標(biāo)準(zhǔn)化的 loop 宏在 Scheme 中就被拋棄了。照進(jìn)現(xiàn)實(shí)前車之鑒,后事之師。Lisp 強(qiáng)大的功能,反面導(dǎo)致了語(yǔ)言的分裂,最終使 Lisp 也慢慢退出歷史舞臺(tái)(主流地位),這也被稱為 The Lisp Curse。而現(xiàn)實(shí)中我們也常常會(huì)被元編程的強(qiáng)大和便捷誘惑,我認(rèn)為使用元編程之前*考慮會(huì)不會(huì)造成更多的分裂。最基本的就是不應(yīng)該自己造語(yǔ)法(DSL)。當(dāng)然,我的出發(fā)點(diǎn)是多人團(tuán)隊(duì),較大的項(xiàng)目,考慮的是整體的發(fā)展。如果是個(gè)人學(xué)習(xí),或者小團(tuán)隊(duì)等,元編程或許能成為你出眾的秘密武器。但大的項(xiàng)目講求的是合作,DSL 造成的分裂實(shí)在是得不償失,尤其是作者離開后,維護(hù)的工作經(jīng)常后繼無(wú)人。近兩年接觸到的 rust 也是提供了宏的支持,雖然不像 Lisp 宏一樣容易編寫,但從功能的角度上依舊特別強(qiáng)大,而且模板宏寫起來(lái)也很容易,于是有人想寫一個(gè)類似 Python 的 dict 語(yǔ)法:let x = dict!( "hello" => "world" ,"hello2" => "world2" );但我個(gè)人并不喜歡這種語(yǔ)法,我認(rèn)為 Clojure 似的語(yǔ)法更簡(jiǎn)潔 dict!("hello": "world")。那在團(tuán)隊(duì)里引入這兩個(gè)宏就會(huì)引起代碼的分裂,后來(lái)人在看代碼時(shí)就會(huì)很困惑。不利于團(tuán)隊(duì)的建設(shè)。*分享在 知乎 上看到的引用:Hygienic Macros and Compile-Time : A first-class macro system, or support for compile-time code execution in general, is something we may consider in future releases. We don’t want the existence of a macro system to be a that reduces the incentive for making the core language great. 表示不希望用宏來(lái)解決語(yǔ)言本身的缺陷。而我的理解是當(dāng)我們希望用宏(或其它元編程手段)時(shí),很可能是我們使用的語(yǔ)言缺少了某些特性,例如 Java 的 lombok 提供的 @Getter/@Setter 等注解,就是因?yàn)?Java 沒有相應(yīng)的語(yǔ)言層面的支持,看看 Kotlin 的支持你就會(huì)明白的。但即便有了宏(或元編程)的支持,你有信心能做出讓整個(gè)團(tuán)隊(duì)都信服的設(shè)計(jì)嗎?如果沒有,*還是慎重為之。寫在*雖然是吐糟,但這篇之間重寫了三次。想表達(dá)的內(nèi)容很多,最終還是把其它的東西刪去, Lisp curse 還是我想真正表達(dá)的東西吧,其它的基礎(chǔ)知識(shí),有緣人自然會(huì)從其它地方學(xué)會(huì)。年輕人容易崇拜力量,我們也別忘了陽(yáng)光還有影子。
3.學(xué)習(xí)python編程需要安裝哪些軟件?
你好,需要安裝Python平臺(tái)和一個(gè)順手的代碼編輯器,Python平臺(tái)2.6、2.7、3.0+的版本皆可,如果你在windows下代碼編輯器可以使用、PythonWin、自帶的IDLE也行,但智能感不夠強(qiáng),在Linux下,可以考慮Vim,如果需要,請(qǐng)追問或私信留下郵箱,我將所需的工具和一些教材發(fā)給你。
就拿大數(shù)據(jù)說(shuō)話,優(yōu)勢(shì)一目了然,從事IT行業(yè),打開IT行業(yè)的新大門,找到適合自己的培訓(xùn)機(jī)構(gòu),進(jìn)行專業(yè)和系統(tǒng)的學(xué)習(xí)。