模塊:python
引用模塊sql
做用域:數據庫
在一個模塊中,咱們可能會定義不少函數和變量,但有的函數和變量咱們但願給別人使用,有的函數和變量咱們但願僅僅在模塊內部使用。在Python中,是經過_
前綴來實現的。編程
正常的函數和變量名是公開的(public),能夠被直接引用,好比:abc
,x123
,PI
等;app
相似__xxx__
這樣的變量是特殊變量,能夠被直接引用,可是有特殊用途,好比上面的__author__
,__name__
就是特殊變量,hello
模塊定義的文檔註釋也能夠用特殊變量__doc__
訪問,咱們本身的變量通常不要用這種變量名;函數
相似_xxx
和__xxx
這樣的函數或變量就是非公開的(private),不該該被直接引用,好比_abc
,__abc
等;單元測試
之因此咱們說,private函數和變量「不該該」被直接引用,而不是「不能」被直接引用,是由於Python並無一種方法能夠徹底限制訪問private函數或變量,可是,從編程習慣上不該該引用private函數或變量。測試
private函數或變量不該該被別人引用,那它們有什麼用呢?請看例子:ui
def _private_1(name): return 'Hello, %s' % name def _private_2(name): return 'Hi, %s' % name def greeting(name): if len(name) > 3: return _private_1(name) else: return _private_2(name)
咱們在模塊裏公開greeting()
函數,而把內部邏輯用private函數隱藏起來了,這樣,調用greeting()
函數不用關心內部的private函數細節,這也是一種很是有用的代碼封裝和抽象的方法,即:spa
外部不須要引用的函數所有定義成private,只有外部須要引用的函數才定義爲public。
當咱們試圖加載一個模塊時,Python會在指定的路徑下搜索對應的.py文件,若是找不到,就會報錯:
>>> import mymodule
Traceback (most recent call last): File "<stdin>", line 1, in <module> ImportError: No module named mymodule
默認狀況下,Python解釋器會搜索當前目錄、全部已安裝的內置模塊和第三方模塊,搜索路徑存放在sys
模塊的path
變量中:
>>> import sys >>> sys.path ['', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python36.zip', '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6', ..., '/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages']
若是咱們要添加本身的搜索目錄,有兩種方法:
一是直接修改sys.path
,添加要搜索的目錄:
>>> import sys >>> sys.path.append('/Users/michael/my_py_scripts')
這種方法是在運行時修改,運行結束後失效。
第二種方法是設置環境變量PYTHONPATH
,該環境變量的內容會被自動添加到模塊搜索路徑中。設置方式與設置Path環境變量相似。注意只須要添加你本身的搜索路徑,Python本身自己的搜索路徑不受影響。
面向對象的OOP
數據封裝、繼承和多態是面向對象的三大特色。向對象的設計思想是從天然界中來的,由於在天然界中,類(Class)和實例(Instance)的概念是很天然的。Class是一種抽象概念,好比咱們定義的Class——Student,是指學生這個概念,而實例(Instance)則是一個個具體的Student,好比,Bart Simpson和Lisa Simpson是兩個具體的Student。因此,面向對象的設計思想是抽象出Class,根據Class建立Instance。面向對象的抽象程度又比函數要高,由於一個Class既包含數據,又包含操做數據的方法。
面向對象最重要的概念就是類(Class)和實例(Instance),必須牢記類是抽象的模板。類是建立實例的模板,而實例則是一個一個具體的對象,各個實例擁有的數據都互相獨立,互不影響;方法就是與實例綁定的函數,和普通函數不一樣,方法能夠直接訪問實例的數據;經過在實例上調用方法,咱們就直接操做了對象內部的數據,但無需知道方法內部的實現細節
訪問限制:
若是要讓內部屬性不被外部訪問,能夠把屬性的名稱前加上兩個下劃線__
,在Python中,實例的變量名若是以__
開頭,就變成了一個私有變量(private),只有內部能夠訪問,外部不能訪問
class Student(object): def __init__(self, name, score): self.__name = name self.__score = score def print_score(self): print('%s: %s' % (self.__name, self.__score)) def get_name(self): return self.__name def get_score(self): return self.__scor
繼承與多態
繼承能夠把父類的全部功能都直接拿過來,這樣就沒必要重零作起,子類只須要新增本身特有的方法,也能夠把父類不適合的方法覆蓋重寫。
動態語言的鴨子類型特色決定了繼承不像靜態語言那樣是必須的。
獲取對象信息:
type判斷對象類型
types中定義的常量函數
instance()函數
dir()函數 獲取一個對象的全部屬性和方法
配合getattr()
、setattr()
以及hasattr()
,咱們能夠直接操做一個對象的狀態
>>> class MyObject(object): ... def __init__(self): ... self.x = 9 ... def power(self): ... return self.x * self.x ... >>> obj = MyObject()
緊接着,能夠測試該對象的屬性:
>>> hasattr(obj, 'x') # 有屬性'x'嗎? True >>> obj.x 9 >>> hasattr(obj, 'y') # 有屬性'y'嗎? False >>> setattr(obj, 'y', 19) # 設置一個屬性'y' >>> hasattr(obj, 'y') # 有屬性'y'嗎? True >>> getattr(obj, 'y') # 獲取屬性'y' 19 >>> obj.y # 獲取屬性'y' 19
實例屬性和類屬性
實例屬性屬於各個實例全部,互不干擾;
類屬性屬於類全部,全部實例共享一個屬性;不要對實例屬性和類屬性使用相同的名字,不然將產生難以發現的錯誤
python容許動態綁定屬性和方法
class Student(object): pass 而後,嘗試給實例綁定一個屬性: >>> s = Student() >>> s.name = 'Michael' # 動態給實例綁定一個屬性 >>> print(s.name) Michael 還能夠嘗試給實例綁定一個方法: >>> def set_age(self, age): # 定義一個函數做爲實例方法 ... self.age = age ... >>> from types import MethodType >>> s.set_age = MethodType(set_age, s) # 給實例綁定一個方法 >>> s.set_age(25) # 調用實例方法 >>> s.age # 測試結果 25
使用__slots__
限制實例的屬性
好比,只容許對Student實例添加name
和age
屬性。
爲了達到限制的目的,Python容許在定義class的時候,定義一個特殊的__slots__
變量,來限制該class實例能添加的屬性:
class Student(object): __slots__ = ('name', 'age') # 用tuple定義容許綁定的屬性名稱 而後,咱們試試: >>> s = Student() # 建立新的實例 >>> s.name = 'Michael' # 綁定屬性'name' >>> s.age = 25 # 綁定屬性'age' >>> s.score = 99 # 綁定屬性'score' Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'Student' object has no attribute 'score'
因爲'score'
沒有被放到__slots__
中,因此不能綁定score
屬性,試圖綁定score
將獲得AttributeError
的錯誤。
使用__slots__
要注意,__slots__
定義的屬性僅對當前類實例起做用,對繼承的子類是不起做用的:
>>> class GraduateStudent(Student): ... pass ... >>> g = GraduateStudent() >>> g.score = 9999
除非在子類中也定義__slots__
,這樣,子類實例容許定義的屬性就是自身的__slots__
加上父類的__slots__
。
Python內置的@property
裝飾器就是負責把一個方法變成屬性調用的
class Student(object): @property def score(self): return self._score @score.setter def score(self, value): if not isinstance(value, int): raise ValueError('score must be an integer!') if value < 0 or value > 100: raise ValueError('score must between 0 ~ 100!') self._score = value
@property
的實現比較複雜,咱們先考察如何使用。把一個getter方法變成屬性,只須要加上@property
就能夠了,此時,@property
自己又建立了另外一個裝飾器@score.setter
,負責把一個setter方法變成屬性賦值,因而,咱們就擁有一個可控的屬性操做
多重繼承
class Dog(Mammal, RunnableMixIn, CarnivorousMixIn): pass
定製類
__str__和__repr__
class Student(object): def __init__(self, name): self.name = name def __str__(self): return 'Student object (name=%s)' % self.name __repr__ = __str__
__iter__、__getattr__、__call__
使用枚舉類:
from enum import Enum Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
value
屬性則是自動賦給成員的int
常量,默認從1
開始計數。
若是須要更精確地控制枚舉類型,能夠從Enum
派生出自定義類:
from enum import Enum, unique @unique class Weekday(Enum): Sun = 0 # Sun的value被設定爲0 Mon = 1 Tue = 2 Wed = 3 Thu = 4 Fri = 5 Sat = 6
@unique
裝飾器能夠幫助咱們檢查保證沒有重複值。
訪問這些枚舉類型能夠有若干種方法:
>>> day1 = Weekday.Mon >>> print(day1) Weekday.Mon >>> print(Weekday.Tue) Weekday.Tue >>> print(Weekday['Tue']) Weekday.Tue >>> print(Weekday.Tue.value) 2 >>> print(day1 == Weekday.Mon) True >>> print(day1 == Weekday.Tue) False >>> print(Weekday(1)) Weekday.Mon >>> print(day1 == Weekday(1)) True >>> Weekday(7) Traceback (most recent call last): ... ValueError: 7 is not a valid Weekday >>> for name, member in Weekday.__members__.items(): ... print(name, '=>', member) ... Sun => Weekday.Sun Mon => Weekday.Mon Tue => Weekday.Tue Wed => Weekday.Wed Thu => Weekday.Thu Fri => Weekday.Fri Sat => Weekday.Sat
可見,既能夠用成員名稱引用枚舉常量,又能夠直接根據value的值得到枚舉常量。
元類:
type()
函數既能夠返回一個對象的類型,又能夠建立出新的類型,好比,咱們能夠經過type()
函數建立出Hello
類,而無需經過class Hello(object)...
的定義。
>>> def fn(self, name='world'): # 先定義函數 ... print('Hello, %s.' % name) ... >>> Hello = type('Hello', (object,), dict(hello=fn)) # 建立Hello class >>> h = Hello() >>> h.hello() Hello, world. >>> print(type(Hello)) <class 'type'> >>> print(type(h)) <class '__main__.Hello'>
要建立一個class對象,type()
函數依次傳入3個參數:
fn
綁定到方法名hello
上。除了使用type()
動態建立類之外,要控制類的建立行爲,還可使用metaclass。
metaclass,直譯爲元類,簡單的解釋就是:
當咱們定義了類之後,就能夠根據這個類建立出實例,因此:先定義類,而後建立實例。
可是若是咱們想建立出類呢?那就必須根據metaclass建立出類,因此:先定義metaclass,而後建立類。
鏈接起來就是:先定義metaclass,就能夠建立類,最後建立實例。
因此,metaclass容許你建立類或者修改類。換句話說,你能夠把類當作是metaclass建立出來的「實例」。
錯誤、調試和測試
try...except...finally
try: print('try...') r = 10 / int('2') print('result:', r) except ValueError as e: print('ValueError:', e) except ZeroDivisionError as e: print('ZeroDivisionError:', e) else: print('no error!') finally: print('finally...') print('END')
出錯的時候,必定要分析錯誤的調用棧信息,才能定位錯誤的位置。
raise
# err_reraise.py def foo(s): n = int(s) if n==0: raise ValueError('invalid value: %s' % s) return 10 / n def bar(): try: foo('0') except ValueError as e: print('ValueError!') raise bar()
Python內置的try...except...finally
用來處理錯誤十分方便。出錯時,會分析錯誤信息並定位錯誤發生的代碼位置纔是最關鍵的。
程序也能夠主動拋出錯誤,讓調用者來處理相應的錯誤。可是,應該在文檔中寫清楚可能會拋出哪些錯誤,以及錯誤產生的緣由。
assert() 斷言
def foo(s): n = int(s) assert n != 0, 'n is zero!' return 10 / n def main(): foo('0')
logging
import logging logging.basicConfig(level=logging.INFO) s = '0' n = int(s) logging.info('n = %d' % n) print(10 / n)
斷點,調試
注意:斷言的開關「-O」是英文大寫字母O,不是數字0。
單元測試:
class Dict(dict): def __init__(self, **kw): super().__init__(**kw) def __getattr__(self, key): try: return self[key] except KeyError: raise AttributeError(r"'Dict' object has no attribute '%s'" % key) def __setattr__(self, key, value): self[key] = value
爲了編寫單元測試,咱們須要引入Python自帶的unittest
模塊,編寫mydict_test.py
以下:
import unittest from mydict import Dict class TestDict(unittest.TestCase): def test_init(self): d = Dict(a=1, b='test') self.assertEqual(d.a, 1) self.assertEqual(d.b, 'test') self.assertTrue(isinstance(d, dict)) def test_key(self): d = Dict() d['key'] = 'value' self.assertEqual(d.key, 'value') def test_attr(self): d = Dict() d.key = 'value' self.assertTrue('key' in d) self.assertEqual(d['key'], 'value') def test_keyerror(self): d = Dict() with self.assertRaises(KeyError): value = d['empty'] def test_attrerror(self): d = Dict() with self.assertRaises(AttributeError): value = d.empty
能夠在單元測試中編寫兩個特殊的setUp()
和tearDown()
方法。這兩個方法會分別在每調用一個測試方法的先後分別被執行。
setUp()
和tearDown()
方法有什麼用呢?設想你的測試須要啓動一個數據庫,這時,就能夠在setUp()
方法中鏈接數據庫,在tearDown()
方法中關閉數據庫,這樣,沒必要在每一個測試方法中重複相同的代碼:
class TestDict(unittest.TestCase): def setUp(self): print('setUp...') def tearDown(self): print('tearDown...')
文檔測試