不管你是待業(yè)還是失業(yè),在這個(gè)被互聯(lián)網(wǎng)圍繞的時(shí)代里,選擇python元編程,就多了一項(xiàng)技能,還怕找不到工作?,還怕不好找工作?小編就來告訴你這個(gè)專業(yè)的優(yōu)勢到底體現(xiàn)在哪里:Python元編程:控制你想控制的一切,不要輕易使用元編程,學(xué)習(xí)python編程需要安裝哪些軟件???。
1.Python元編程:控制你想控制的一切
很多人不理解“元編程”是個(gè)什么東西,關(guān)于它也沒有一個(gè)十分準(zhǔn)確的定義。這篇文章要說的是Python里的元編程,實(shí)際上也不一定就真的符合“元編程”的定義。只不過我無法找到一個(gè)更準(zhǔn)確的名字來代表這篇文章的主題,所以就借了這么一個(gè)名號。副標(biāo)題是控制你想控制的一切,實(shí)際上這篇文章講的都是一個(gè)東西,利用Python提供給我們的特性,盡可能的使代碼優(yōu)雅簡潔。具體而言,通過編程的方法,在更高的抽象層次上對一種層次的抽象的特性進(jìn)行修改。首先說,Python中一切皆對象,老生常談。還有,Python提供了許多特殊方法、元類等等這樣的“元編程”機(jī)制。像給對象動(dòng)態(tài)添加屬性方法之類的,在Python中根本談不上是“元編程”,但在某些靜態(tài)語言中卻是需要一定技巧的東西。我們來談些Python程序員也容易被搞糊涂的東西。我們先來把對象分分層次,通常我們知道一個(gè)對象有它的類型,老早以前Python就將類型也實(shí)現(xiàn)為對象。這樣我們就有了實(shí)例對象和類對象。這是兩個(gè)層次。稍有基礎(chǔ)的讀者就會(huì)知道還有元類這個(gè)東西的存在,簡言之,元類就是“類”的“類”,也就是比類更高層次的東西。這又有了一個(gè)層次。還有嗎? vs RunTime如果我們換個(gè)角度,不用非得和之前的三個(gè)層次使用同樣的標(biāo)準(zhǔn)。我們再來區(qū)分兩個(gè)東西:和RunTime,它們之間也并非界限分明,顧名思義,就是兩個(gè)時(shí)刻,導(dǎo)入時(shí)和運(yùn)行時(shí)。當(dāng)一個(gè)模塊被導(dǎo)入時(shí),會(huì)發(fā)生什么?在全局作用域的語句(非定義性語句)被執(zhí)行。函數(shù)定義呢?一個(gè)函數(shù)對象被創(chuàng)建,但其中的代碼不會(huì)被執(zhí)行。類定義呢?一個(gè)類對象被創(chuàng)建,類定義域的代碼被執(zhí)行,類的方法中的代碼自然也不會(huì)被執(zhí)行。執(zhí)行時(shí)呢?函數(shù)和方法中的代碼會(huì)被執(zhí)行。當(dāng)然你要先調(diào)用它們。元類所以我們可以說元類和類是屬于的,import一個(gè)模塊之后,它們就會(huì)被創(chuàng)建。實(shí)例對象屬于RunTime,單import是不會(huì)創(chuàng)建實(shí)例對象的。不過話不能說的太絕對,因?yàn)槿绻阋窃谀K作用域?qū)嵗?,?shí)例對象也是會(huì)被創(chuàng)建的。只不過我們通常把它們寫在函數(shù)里面,所以這樣劃分。如果你想控制產(chǎn)生的實(shí)例對象的特性該怎么做?太簡單了,在類定義中重寫__init__方法。那么我們要控制類的一些性質(zhì)呢?有這種需求嗎?當(dāng)然有!經(jīng)典的單例模式,大家都知道有很多種實(shí)現(xiàn)方式。要求就是,一個(gè)類只能有一個(gè)實(shí)例。最簡單的實(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)雅。我們再來審視一下需求,要一個(gè)類只能有一個(gè)實(shí)例。我們在類中定義的方法都是實(shí)例對象的行為,那么要想改變類的行為,就需要更高層次的東西。元類在這個(gè)時(shí)候登場在合適不過了。前面說過,元類是類的類。也就是說,元類的__init__方法就是類的初始化方法。 我們知道還有__call__這個(gè)東西,它能讓實(shí)例像函數(shù)那樣被調(diào)用,那么元類的這個(gè)方法就是類在被實(shí)例化時(shí)調(diào)用的方法。代碼就可以寫出來了: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í)例。也就是說,type是所有類的類,也就是最基本的元類,它規(guī)定了一些所有類在產(chǎn)生時(shí)需要的一些操作。所以我們的自定義元類需要子類化type。同時(shí)type也是一個(gè)對象,所以它又是object的子類。有點(diǎn)不太好理解,大概知道就可以了。裝飾器我們再來說說裝飾器。大多數(shù)人認(rèn)為裝飾器是Python里面最難理解的概念之一。其實(shí)它不過就是一個(gè)語法糖,理解了函數(shù)也是對象之后。就可以很輕易的寫出自己的裝飾器了。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,它是用來讓我們返回的內(nèi)部函數(shù)wrapper和原來的函數(shù)擁有相同的函數(shù)簽名的,基本上我們在寫裝飾器時(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)。對數(shù)據(jù)的抽象--描述符如果我們想讓某一些類擁有某些相同的特性,或者說可以實(shí)現(xiàn)在類定義對其的控制,我們可以自定義一個(gè)元類,然后讓它成為這些類的元類。如果我們想讓某一些函數(shù)擁有某些相同的功能,又不想把代碼復(fù)制粘貼一遍,我們可以定義一個(gè)裝飾器。那么,假如我們想讓實(shí)例的屬性擁有某些共同的特點(diǎn)呢?有人可能會(huì)說可以用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__的寫法,基本上對instance的判斷是必需的,不然會(huì)報(bào)錯(cuò)。原因也不難理解,就不細(xì)說了??刂谱宇惖膭?chuàng)建——代替元類的方法在Python3.6中,我們可以通過實(shí)現(xiàn)__init_subclass__特殊方法,來自定義子類的創(chuàng)建,這樣我們就可以在某些情況下擺脫元類這個(gè)討厭的東西。class : = [] def __init_subclass__(cls, **kwargs): super().__init_subclass__(**kwargs) cls..append(cls) class Plugin1(): pass class Plugin2(): pass小結(jié)諸如元類等元編程對于大多數(shù)人來說有些晦澀難懂,大多數(shù)時(shí)候也無需用到它們。但是大多數(shù)框架背后的實(shí)現(xiàn)都使用到了這些技巧,這樣才能讓使用者寫出來的代碼簡潔易懂。如果你想更深入的了解這些技巧,可以參看一些書籍例如《Fluent Python》、《Python Cookbook》(這篇文章有的內(nèi)容就是參考了它們),或者看官方文檔中的某些章節(jié)例如上文說的描述符HowTo,還有Data Model一節(jié)等等。或者直接看Python的源碼,包括用Python寫的以及CPython的源碼。記住,只有在充分理解了它們之后再去使用,也不要是個(gè)地方就想著使用這些技巧。
2.不要輕易使用元編程
本文轉(zhuǎn)載自: Common Lisp 里的 loop 宏就。一方面,它體現(xiàn)了宏的強(qiáng)大;另一方面,它展現(xiàn)了宏能給我們帶來的復(fù)雜。熟悉 C/Java 語言都知道循環(huán)是語言本身提供的關(guān)鍵字,一般是 for。但 Lisp 語言特別精簡,它認(rèn)為循環(huán)只是遞歸的一個(gè)特殊形式,語言本身也不包含任何的循環(huán)關(guān)鍵字。于是有人用宏實(shí)現(xiàn)了 loop ,它讓我們能以近乎英語的方式在 Lisp 里寫循環(huán)語句,這里從 這里 摘抄一個(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 這樣的語法并不是 Lisp 提供的,而是 loop 宏實(shí)現(xiàn)的,這些語法離開了 loop 也就不再合法。我們看到 loop 宏讓我們能在 Lisp 語言不支持的情況下享受到近乎現(xiàn)代語言中才包含的 for ... in ... 語法。要知道在 Java 中有兩種 for 語句: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)越性,卻也只能無奈等到語言支持才行。而 Lisp 程序員很快就能通過宏來實(shí)現(xiàn)自己理想中的語法。然而光明與黑暗共生,宏給我們帶來極大自由的同時(shí),也意味著分裂。每個(gè)程序員心中理想的語法各不相同,這就意味著一千個(gè)程序員會(huì)有一千種語法。在 Lisp 中宏是非常容易編寫的(不代表容易正確編寫),意味著真的會(huì)存在一千種語法,大家誰也不服誰,因此造成分裂;但在 C/Java 中,沒有宏的支持,雖然有一千種想法,但大家都寫不出編譯器,于是只能集中討論,統(tǒng)一語法了,再靠大牛們實(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)致了語言的分裂,最終使 Lisp 也慢慢退出歷史舞臺(tái)(主流地位),這也被稱為 The Lisp Curse。而現(xiàn)實(shí)中我們也常常會(huì)被元編程的強(qiáng)大和便捷誘惑,我認(rèn)為使用元編程之前*考慮會(huì)不會(huì)造成更多的分裂。最基本的就是不應(yīng)該自己造語法(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)常后繼無人。近兩年接觸到的 rust 也是提供了宏的支持,雖然不像 Lisp 宏一樣容易編寫,但從功能的角度上依舊特別強(qiáng)大,而且模板宏寫起來也很容易,于是有人想寫一個(gè)類似 Python 的 dict 語法:let x = dict!( "hello" => "world" ,"hello2" => "world2" );但我個(gè)人并不喜歡這種語法,我認(rèn)為 Clojure 似的語法更簡潔 dict!("hello": "world")。那在團(tuán)隊(duì)里引入這兩個(gè)宏就會(huì)引起代碼的分裂,后來人在看代碼時(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. 表示不希望用宏來解決語言本身的缺陷。而我的理解是當(dāng)我們希望用宏(或其它元編程手段)時(shí),很可能是我們使用的語言缺少了某些特性,例如 Java 的 lombok 提供的 @Getter/@Setter 等注解,就是因?yàn)?Java 沒有相應(yīng)的語言層面的支持,看看 Kotlin 的支持你就會(huì)明白的。但即便有了宏(或元編程)的支持,你有信心能做出讓整個(gè)團(tuán)隊(duì)都信服的設(shè)計(jì)嗎?如果沒有,*還是慎重為之。寫在*雖然是吐糟,但這篇之間重寫了三次。想表達(dá)的內(nèi)容很多,最終還是把其它的東西刪去, Lisp curse 還是我想真正表達(dá)的東西吧,其它的基礎(chǔ)知識(shí),有緣人自然會(huì)從其它地方學(xué)會(huì)。年輕人容易崇拜力量,我們也別忘了陽光還有影子。
3.學(xué)習(xí)python編程需要安裝哪些軟件?
你好,需要安裝Python平臺(tái)和一個(gè)順手的代碼編輯器,Python平臺(tái)2.6、2.7、3.0+的版本皆可,如果你在windows下代碼編輯器可以使用、PythonWin、自帶的IDLE也行,但智能感不夠強(qiáng),在Linux下,可以考慮Vim,如果需要,請追問或私信留下郵箱,我將所需的工具和一些教材發(fā)給你。
就拿大數(shù)據(jù)說話,優(yōu)勢一目了然,從事IT行業(yè),打開IT行業(yè)的新大門,找到適合自己的培訓(xùn)機(jī)構(gòu),進(jìn)行專業(yè)和系統(tǒng)的學(xué)習(xí)。