工做久了對以前的基礎語法作一個整理,以便本身更瞭解python的語法, 在編碼的時候能夠更加更加駕輕就熟。html
先看兩個例子前端
In [20]: a = 1
In [21]: id(a)
Out[21]: 4482033600
In [22]: def func(a):
...: a = 2
...: print(id(a), a)
...:
In [23]: func(a)
4482033632 2
In [24]:
複製代碼
全部的變量均可以理解是內存中一個對象的「引用」, 經過id查看變量內存地址java
在命名變量a查看的id和在函數中變量a的id是不同的, 因此做用域的不一樣會生成兩個不一樣的內存地址python
In [24]: b = 1
In [25]: def fun(b):
...: b = 2
...: print(id(b), b)
...:
In [26]: fun(b)
4482033632 2
In [27]:
複製代碼
在定義b變量以後查看id 和上一個a是相似的關係, 可是當查看全局變量b的時候發現id和第一個例子中函數中a變量的內存地址相同, 因此在python中當值類型相同時, 指針會指向同一個內存地址以減小內存的使用。面試
在Python中有常見的string, int, list,float, tuple, dic算法
其中不可變對象: string, int , float, tuple, 可變對象: list, dictionary編程
在python中變量存放的是對象引用,雖然對象引用是可變的, 可是對象自己是不變的, 指針不會指向新的內存空間.設計模式
在python中方法的類型有靜態方法, 類方法和實例方法安全
代碼以下:bash
def foo(x):
print(x)
class A(object):
""" 建立類 """
def foo(self, x):
# 實例方法
...
@classmethod
def class_foo(cls, x)
# 類方法
...
@staticmethod
def static_foo(x):
# 靜態方法
...
a = A()
a.foo(1)
A.class_foo(1)
A.static(1)
a.class_foo(1)
a.static(1)
複製代碼
建立A類的時候實例方法必須是要建立一個實例a, 而且經過實例a調用實例方法, 可是不能直接用過A類直接調用, 可是實例a經過也能夠調用A類的靜態方法和類方法。
類變量: 在建立類的時候定義,而且是全部實例之間共享的值.
實例變量: 實例化以後, 每一個實例單獨擁有的變量,在建立實例中傳值
In [40]: class A(object):
...: num = 0
...: def __init__(self, name):
...: self.name = name
...: A.num += 1
...: print(name, A.num)
...:
...:
...:
In [41]: a = A('jay')
jay 1
In [42]: b = A('jack')
jack 2
In [43]: A.num
Out[43]: 2
In [44]:
複製代碼
在定義變量的時候不用聲明變量類型, 在程序運行時須要動態獲取對象類型, type(),dir(),getattr(),hasattr(),isinstance().
v = {key: value for (key, value) in iterable }
l = [x for x in list]
複製代碼
In [45]: class B(object):
...: def __init__(self):
...: self.__superprivate = 'hello'
...: self._semprivate = 'word'
...:
In [46]: m = B()
In [49]: m.__dict__
Out[49]: {'_B__superprivate': 'hello', '_semprivate': 'word'}
複製代碼
雙下劃線__foo__ 類型: python內部函數的命名方式,用來區別其餘用戶自定義的命名衝突,常見的__init__,__new__等等,
_foo: 類的私有變量命名方式,在該類被實例化或者被引用的時候訪問不到的變量
__foo: 解析器會用_classname__foo來替代這個名字,以區別和其餘類重複命名的衝突,經過對象名._類名__xxx這樣的方式能夠訪問.
在建立列表時,因爲受到內存限制, 列表容量有限, 當一次性生成大量數據的列表時會形成大量的內存浪費, 因此在python中採用生成器: 邊循環邊計算的機制 -> generator
>>> L = [x*x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x*x for x in range(10))
>>> g
<generator object <genexpr> at 0x0000028F8B774200>
複製代碼
用*args和**kwargs只是爲了方便並無強制使用它們.
當你不肯定你的函數裏將要傳遞多少參數時你能夠用*args.例如,它能夠傳遞任意數量的參數:
>>> def print_everything(*args):
for count, thing in enumerate(args):
... print '{0}. {1}'.format(count, thing)
...
>>> print_everything('apple', 'banana', 'cabbage')
0. apple
1. banana
2. cabbage
類似的,**kwargs容許你使用沒有事先定義的參數名:
>>> def table_things(**kwargs):
... for name, value in kwargs.items():
... print '{0} = {1}'.format(name, value)
...
>>> table_things(apple = 'fruit', cabbage = 'vegetable')
cabbage = vegetable
apple = fruit
你也能夠混着用.命名參數首先得到參數值而後全部的其餘參數都傳遞給*args和**kwargs.命名參數在列表的最前端.例如:
def table_things(titlestring, **kwargs)
複製代碼
args和**kwargs能夠同時在函數的定義中,可是args必須在**kwargs前面.
當調用函數時你也能夠用*和**語法.例如:
>>> def print_three_things(a, b, c):
... print 'a = {0}, b = {1}, c = {2}'.format(a,b,c)
...
>>> mylist = ['aardvark', 'baboon', 'cat']
>>> print_three_things(*mylist)
a = aardvark, b = baboon, c = cat
複製代碼
就像你看到的同樣,它能夠傳遞列表(或者元組)的每一項並把它們解包.注意必須與它們在函數裏的參數相吻合.固然,你也能夠在函數定義或者函數調用時用*.
裝飾器是一個很是有用的設計, 常常被用於有切面需求的場景, 較爲經典的的插入日誌,性能測試、事務處理等。能夠抽離出大量函數中與函數自己無關的雷同代碼病繼續重用, 其實就是給函數添加額外功能。
「當看到一隻鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那麼這隻鳥就能夠被稱爲鴨子。」
咱們並不關心對象是什麼類型,究竟是不是鴨子,只關心行爲。
好比在python中,有不少file-like的東西,好比StringIO,GzipFile,socket。它們有不少相同的方法,咱們把它們看成文件使用。
又好比list.extend()方法中,咱們並不關心它的參數是否是list,只要它是可迭代的,因此它的參數能夠是list/tuple/dict/字符串/生成器等.
鴨子類型在動態語言中常用,很是靈活,使得python不想java那樣專門去弄一大堆的設計模式。
之因此要重載, 主要是爲了改變可變參數類型或者可變參數個數,面向對象思想了解的狀況下很容易理解重載。
這篇文章很好的介紹了新式類的特性: www.cnblogs.com/btchenguang…
新式類很早在2.2就出現了,因此舊式類徹底是兼容的問題,Python3裏的類所有都是新式類.這裏有一個MRO問題能夠了解下(新式類繼承是根據C3算法,舊式類是深度優先),<Python核心編程>裏講的也不少.
一箇舊式類的深度優先的例子
class A():
def foo1(self):
print "A"
class B(A):
def foo2(self):
pass
class C(A):
def foo1(self):
print "C"
class D(B, C):
pass
d = D()
d.foo1()
# A
複製代碼
按照經典類的查找順序從左到右深度優先的規則,在訪問d.foo1()的時候,D這個類是沒有的..那麼往上查找,先找到B,裏面沒有,深度優先,訪問A,找到了foo1(),因此這時候調用的是A的foo1(),從而致使C重寫的foo1()被繞過
new__是一個靜態方法,而__init__是一個實例方法. new__方法會返回一個建立的實例,而__init__什麼都不返回. 只有在__new__返回一個cls的實例時後面的__init__才能被調用. 當建立一個新實例時調用__new,初始化一個實例時用__init.
ps: metaclass__是建立類時起做用.因此咱們能夠分別使用__metaclass,__new__和__init__來分別在類建立,實例建立和實例初始化的時候作一些小手腳.
單例模式是一種軟件經常使用的設計模式, 在它核心結構中只包含一個被稱爲單例類的特殊類。經過單例模式能夠保證一個類中只有一個實例而且這個實例易於被外界訪問, 從而對實例個數的控制而且節約系統內存資源 new()在__init__()以前被調用,用於生成實例對象。利用這個方法和類的屬性的特色能夠實現設計模式的單例模式。單例模式是指建立惟一對象,單例模式設計的類只能實例 這個絕對常考啊.絕對要記住1~2個方法,當時面試官是讓手寫的.
單例模式的實現方法:
class Singleton(object):
def __new__(cls, *args, **kwargs):
if not hasttr(cls, '_instance'):
orig = super(Singleton, cls)
cls._instance = orig.__new__(cls, *args, **kwargs)
return cls._instance
class MyClass(Singleton):
a = 1
複製代碼
class Borg(object):
_state = {}
def __new__(cls, *args, **kw):
ob = super(Borg, cls).__new__(cls, *args, **kw)
ob.__dict__ = cls._state
return ob
class MyClass2(Borg):
a = 1
複製代碼
def singleton(cls):
instances = {}
def getinstance(*args, **kw):
if cls not in instances:
instances[cls] = cls(*args, **kw)
return instances[cls]
return getinstance
@singleton
class MyClass:
...
複製代碼
# mysingleton.py
class My_Singleton(object):
def foo(self):
pass
my_singleton = My_Singleton()
# to use
from mysingleton import my_singleton
my_singleton.foo()
複製代碼
Python中, 一個變量的做用域老是在代碼中被賦值的地方所決定的。
當Python遇到一個變量的話他會按照這樣的順序進行搜索
本地做用域(local) -> 當前做用域被嵌入的本地做用域( Enclosing locals) -> 全局/模塊做用域(built-in)
線程全局鎖(Global Interpreter Lock),即Python爲了保證線程安全而採起的獨立線程運行的限制,說白了就是一個核只能在同一時間運行一個線程.對於io密集型任務,python的多線程起到做用,但對於cpu密集型任務,python的多線程幾乎佔不到任何優點,還有可能由於爭奪資源而變慢。
解決辦法就是多進程和下面的協程(協程也只是單CPU,可是能減少切換代價提高性能).
簡單點說協程是進程和線程的升級版,進程和線程都面臨着內核態和用戶態的切換問題而耗費許多切換時間,而協程就是用戶本身控制切換的時機,再也不須要陷入系統的內核態.
閉包(closure)是函數式編程的重要的語法結構。閉包也是一種組織代碼的結構,它一樣提升了代碼的可重複使用性。
當一個內嵌函數引用其外部做做用域的變量,咱們就會獲得一個閉包. 總結一下,建立一個閉包必須知足如下幾點:
必須有一個內嵌函數 內嵌函數必須引用外部函數中的變量 外部函數的返回值必須是內嵌函數 感受閉包仍是有難度的,幾句話是說不明白的,仍是查查相關資料.
重點是函數運行後並不會被撤銷,就像16題的instance字典同樣,當函數運行完後,instance並不被銷燬,而是繼續留在內存空間裏.這個功能相似類裏的類變量,只不過遷移到了函數上.
閉包就像個空心球同樣,你知道外面和裏面,但你不知道中間是什麼樣.