python基礎3.0

模塊:python

引用模塊sql

做用域:數據庫

在一個模塊中,咱們可能會定義不少函數和變量,但有的函數和變量咱們但願給別人使用,有的函數和變量咱們但願僅僅在模塊內部使用。在Python中,是經過_前綴來實現的。編程

正常的函數和變量名是公開的(public),能夠被直接引用,好比:abcx123PI等;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實例添加nameage屬性。

爲了達到限制的目的,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__

使用@property

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個參數:

  1. class的名稱;
  2. 繼承的父類集合,注意Python支持多重繼承,若是隻有一個父類,別忘了tuple的單元素寫法;
  3. class的方法名稱與函數綁定,這裏咱們把函數fn綁定到方法名hello上。

metaclass

除了使用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()方法有什麼用呢?設想你的測試須要啓動一個數據庫,這時,就能夠在setUp()方法中鏈接數據庫,在tearDown()方法中關閉數據庫,這樣,沒必要在每一個測試方法中重複相同的代碼:

class TestDict(unittest.TestCase):

    def setUp(self):
        print('setUp...')

    def tearDown(self):
        print('tearDown...')

 文檔測試

相關文章
相關標籤/搜索