天天看點

Python第九周 學習筆記(1)描述器連結清單異常處理子產品化包管理插件化開發基礎知識補充git伺服器搭建

描述器

get(self, instance, owner)

  • 通路屬性時調用

set(self, instance, value)

  • 當對屬性指派時調用

delete(self, instance)

  • 删除屬性時調用
    • self指代目前執行個體
    • instance是owner的執行個體
    • owner是屬性的所屬的類
  • 描述器實作前提是描述器類執行個體作為類屬性
  • 當隻實作get時 (非資料描述符),屬性查找順序是本執行個體優先,get方法次之
  • 當實作get和set時(資料描述符) ,屬性查找順序是get方法優先

本質

  • 給類添加描述器時可以顯示添加類屬性,或者用setattr注入
    注意所謂的類屬性不僅僅類似與x=A()的屬性,類中定義的函數也是類屬性

模拟staticmethod與classmethod

from functools import partial

class StaticMethod:
    def __init__(self, fn):
        self.fn = fn

    def __get__(self, instance, owner):
        return self.fn

class ClassMethod:
    def __init__(self, fn):
        self.fn = fn

    def __get__(self, instance, owner):
        return partial(self.fn, owner)

class Test:

    @StaticMethod
    def s_mtd():  # s_mtd = StaticMethod(s_mtd)
        print('s_mtd')

    @ClassMethod
    def c_mtd(cls):  # c_mtd = ClassMethod(c_mtd)
        print('c_mtd', cls)

if __name__ == '__main__':
    Test.s_mtd()
    Test.c_mtd()           

模拟property

class Property:
    def __init__(self, fget=None, fset=None):
        self.fget = fget
        self.fset = fset

    def __get__(self, instance, owner):
        return self.fget(instance)

    def __set__(self, instance, value):
        self.fset(instance, value)

    def getter(self):
        pass

    def setter(self, fset):
        self.fset = fset
        return self

class Person:
    def __init__(self, name, age):
        self._name = name
        self._age = age

    @Property
    def name(self):  # name=Property(name)
        return self._name

    @name.setter
    def name(self, value):  # name=Property(name).setter(name)    (value)
        self._name = value

    @Property
    def age(self):  # name=Property(name)
        return self._age

    @age.setter
    def age(self, value):  # name=Property(name).setter(name)    (value)
        self._age = value
           

校驗參數類型

普通裝飾器

import inspect

class TypeCheck:
    def __init__(self, key, type):
        print('TC init')
        self.key = key
        self.type = type

    def __get__(self, instance, owner):
        print('TC get')
        if instance is not None:
            return instance.__dict__[self.key]
        return self

    def __set__(self, instance, value):
        print('TC set')
        if not isinstance(value, self.type):
            raise TypeError
        instance.__dict__[self.key] = value

def typeassert(cls):
    params = inspect.signature(cls).parameters
    for name, type in params.items():
        if type != type.empty:
            setattr(cls, name, type.annotation)
    return cls

@typeassert
class Person:
    name = TypeCheck('name', str)
    age = TypeCheck('age', int)

    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age

tom = Person('tom', 12)
print(tom.name)           

類裝飾器

import inspect

class TypeCheck:
    def __init__(self, key, type):
        print('TC init')
        self.key = key
        self.type = type

    def __get__(self, instance, owner):
        print('TC get')
        if instance is not None:
            return instance.__dict__[self.key]
        return self

    def __set__(self, instance, value):
        print('TC set')
        if not isinstance(value, self.type):
            raise TypeError
        instance.__dict__[self.key] = value

class TypeAssert:
    def __init__(self, cls):
        self.cls = cls

    def __call__(self, name, age):
        params = inspect.signature(self.cls).parameters
        for key, type in params.items():
            if type != type.empty:
                setattr(self.cls, key, TypeCheck(key, type.annotation))
        return self.cls(name, age)

@TypeAssert
class Person:  # Person = TypeAssert(Person)
    name = TypeCheck('name', str)
    age = TypeCheck('age', int)

    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age

tom = Person('tom', '12')
print(tom.name)           

連結清單

class Node:
    """
    Description: Node Class

    attr item: current Node`s data
    attr next: points to the next Node
    attr past: points to the last Node
    """

    def __init__(self, item: object):
        self.__item = item
        self.__next = None
        self.__past = None

    @property
    def item(self):
        return self.__item

    @item.setter
    def item(self, value):
        self.__item = value

    @property
    def next(self):
        return self.__next

    @next.setter
    def next(self, value: object):
        self.__next = value

    @property
    def past(self):
        return self.__past

    @past.setter
    def past(self, value: object):
        self.__past = value

class LinkedList:
    """
    Description: Base class LinkedList

    """

    def __init__(self):
        self.cur = None
        self.head = None
        self.length = 0

    def append(self, no: object):
        raise Exception('Base Method')

    def iternodes(self):
        raise Exception('Base Method')

    def pop(self):
        raise Exception('Base Method')

    def insert(self, position: int, value: object):
        raise Exception('Base Method')

    def remove(self, value: object):
        raise Exception('Base Method')

class SingleLinkedList(LinkedList):
    """
    Description:

    attr head: head Node
    attr cur: current Node

    method append(): append Node
    """

    def __init__(self):
        super().__init__()

    def __iter__(self):
        cur_node = self.head

        while True:
            yield cur_node.item
            if not cur_node.next:
                break
            cur_node = cur_node.next

    def __getitem__(self, item):
        cur_node = self.head

        if isinstance(item, slice):
            pass
        else:
            for _ in range(item):
                cur_node = cur_node.next
            return cur_node.item

    def __setitem__(self, key, value):
        cur_node = self.head

        for _ in range(key):
            cur_node = cur_node.next
        cur_node.item = value

    def append(self, no: object):
        if self.length == 0:
            self.cur = Node(no)
            self.head = self.cur
        else:
            self.cur.next = Node(no)
            self.cur = self.cur.next
        self.length += 1

sl = SingleLinkedList()
sl.append(1)
sl.append(2)
for i in sl:
    print(i)
sl[1] = 999
sl[0] = 234
for i in sl:
    print(i)

class DoubleLinkedList(LinkedList):
    """
    Description:

    attr head:
    attr cur:

    method append:
    method pop:
    method insert:
    method remove:
    method iternodes:
    """

    def __init__(self):
        super().__init__()

    def __iter__(self):
        cur_node = self.head

        while True:
            yield cur_node.item
            if not cur_node.next:
                break
            cur_node = cur_node.next

    def __reversed__(self):
        cur_node = self.cur

        while True:
            yield cur_node.item
            if not cur_node.past:
                break
            cur_node = cur_node.past

    def __getitem__(self, item):
        cur_node = self.head

        if isinstance(item, slice):
            pass
        else:
            for _ in range(item):
                cur_node = cur_node.next
            return cur_node.item

    def __setitem__(self, key, value):
        cur_node = self.head

        for _ in range(key):
            cur_node = cur_node.next
        cur_node.item = value

    def append(self, no: object):
        if self.length == 0:
            self.cur = Node(no)
            self.head = self.cur
        else:
            temp = self.cur
            self.cur.next = Node(no)
            self.cur = self.cur.next
            self.cur.past = temp
        self.length += 1

    def pop(self):
        pop_node = self.cur
        pop_node.past.next = None
        self.cur = self.cur.past

        self.length -= 1
        return pop_node

    def insert(self, position: int, value: object):
        cur_node = self.head
        new_node = Node(value)

        for _ in range(position - 1):
            cur_node = cur_node.next

        next_node = cur_node.next

        cur_node.next = new_node
        new_node.past = cur_node

        new_node.next = next_node
        next_node.past = new_node

    def remove(self, value: object):
        cur_node = self.head

        while True:
            if cur_node.item == value:
                cur_node.past.next = cur_node.next
                cur_node.next.past = cur_node.past
                break
            elif not cur_node.next:
                raise Exception('NodeNotFound')
            cur_node = cur_node.next

    def iternodes(self, *, reverse=False):
        if not reverse:
            cur_node = self.head

            while True:
                yield cur_node.item
                if not cur_node.next:
                    break
                cur_node = cur_node.next
        else:
            cur_node = self.cur

            while True:
                yield cur_node.item
                if not cur_node.past:
                    break
                cur_node = cur_node.past           

異常處理

産生異常

raise 異常執行個體           
  • Python解釋器自己檢測到異常并引發它

異常捕獲

try:
待捕獲異常的代碼塊
except [異常類型] as e:
異常的處理代碼塊
else:
...
finally:
...           
  • e為異常的執行個體
  • 可寫多個except
  • else 沒有任何異常發生則執行
  • finally語句塊無論如何都會執行

BaseException

  • 所有内建異常類的基類

SystemExit

  • sys.exit()引發的異常,異常不捕獲處理,直接交給Python解釋器,解釋器退出

KeyboardInterrupt

  • 指令行使用Ctrl+C終端操作

Exception

  • 所有内建、非系統退出的異常的基類,自定義異常需要繼承它

SyntaxError文法錯誤

  • 此錯誤不可捕獲

ArithmeticError

  • 算術計算錯誤,子類有除零異常等

LookupError

  • 使用映射的鍵或序列的索引無效時引發的異常的基類:IndexError,KeyError

子產品化

import ... 與import ... as ...

  • 找到制定的子產品,加載和初始化它,生成子產品對象
  • 在import所在的作用域的局部命名空間中,增加名稱和上一步建立的對象關聯
  • 導入頂級子產品,其名稱會加入到本地名詞空間中(dir()),并綁定到其子產品對象
  • 導入非頂級子產品,至将其頂級子產品名稱加入到本地名詞空間中,導入的子產品必須用完全限定名通路
  • 如果使用as,as後的名稱直接綁定到導入的子產品對象中,并将該名稱加入到本地名詞空間中

from ... import ...與from ... import ... as ...

  • from後指定的子產品名隻加載并初始化,不導入
  • 對于import子句後的名稱
  • 先查from導入的子產品是否具有該名稱屬性,如果不是,則嘗試導入該名稱的子子產品

自定義子產品

  • 命名規範
  • 全小寫,下劃線來分割

子產品搜尋順序

sys.path

  • 傳回清單
  • 可被修改,追加新路徑

路徑查找順序

  • 程式主目錄
  • PYTHONPATH目錄
  • 标準庫目錄

sys.modules

  • 傳回字典
  • 記錄所有加載的子產品

子產品運作

name

  • 子產品名,如不指定就是檔案名
  • 解釋器初始化時會初始化sys.module字典,建立builtins子產品、main子產品、sys子產品,sys.path

if name == 'main'

  • 用于子產品功能測試
  • 避免主子產品變更的副作用

子產品的屬性

  • file 源檔案路徑
  • cached 編譯後的位元組碼檔案路徑
  • spec 顯示子產品規範
  • name 子產品名
  • package 當子產品是包,同name,否則可以設定為頂級子產品的空字元串

包 Package

  • 目錄下有一個init.py檔案,導入包時,此檔案内容代表此包

子子產品

  • 包目錄下的py檔案、子目錄都是其子子產品

子產品和包總結

  • 導入子子產品一定會加載父子產品,導入父子產品一定不會導入子子產品

    包是特殊的子產品,包含path屬性

絕對導入,相對導入

絕對導入

  • 總是去搜尋子產品搜尋路徑中找

相對導入

  • 隻能在包内使用,且隻能用在from中
  • . 表示目前目錄
  • .. 表示上一級目錄
  • ... 表示上上級目錄

通路控制

  • from ... import *
    • 使用此方法導入子產品時,以_和__開頭的屬性不會導入
  • 使用all
    • 一個清單,每個元素都是子產品内的變量名
    • 定義all後,from ... import * 隻導入all内的屬性

包管理

  • setuptools
    • 包管理的核心子產品
  • pip
    • 目前包管理的事實标準
  • wheel
    • 以二進制安裝,不需要本地編譯
pip install wheel           

建立setup.py檔案

# from distutils.core import setup  # 可能失敗
from setuptools import setup

setup(name='Distutils',
      version='1.0',
      description='Python Distribution Utilities',
      author='Greg Ward',
      author_email='[email protected]',
      url='https://www.python.org/sigs/distutils-sig/',
      packages=['distutils', 'distutils.command'],
     )           
  • package内容是要管理的包

查詢指令幫助

python setup.py cmd -help           

build

  • 建立一個build目錄
python setup.py build           
  • build得到的檔案,直接拷貝到其他項目就可以用

install

python setup.py install           
  • 如果沒有build,會先build編譯,然後安裝

sdist

python setup.py sdist           
  • 建立源代碼的分發包
  • 在其他地方解壓縮這個檔案,裡面有setup.py,就可以使用python setup.py install安裝,也可以
  • pip install XXX.zip直接使用pip安裝這個壓縮包

插件化開發

動态導入

  • 運作時,根據使用者需求,找到子產品的資源動态加載起來
  • import(name, globals=None, locals=None, fromlist=(), level=0)
    • import 本質上就是調用此函數(sys = impot('sys') 等價于import sys),但建議不直接使用,建議使用
    • importlib.import_module(name, package=None)
    • 支援絕對導入和相對導入,如果是相對導入,package必須設定

插件化程式設計技術

依賴的技術

  • 反射:運作時擷取類型的資訊,可以動态維護類型資料
  • 動态import:使用importlib
  • 多線程:可以開啟一個線程,等待使用者輸入,進而加載指定名稱的子產品

加載時機

  1. 程式啟動時
  2. 程式運作中
  • 如插件過多,會導緻程式啟動很慢,如果使用者需要時再加載,如果插件太大或依賴多,插件也會啟動慢。
  • 是以先加載必須、常用插件,其他插件使用時,發現需要,動态載入

基礎知識補充

slot

  • 字典為了查詢效率,必須用空間換時間
  • 如果執行個體對象數量過大,那字典占用空間過大
  • 如果類定義了slot,執行個體會省略建立dict
  • 如果給執行個體增加不在slot的屬性會抛出Attribute異常
  • slot可以定義為元組或清單,通常用元組,省空間
  • slot不會繼承給子類

未實作和未實作異常

  • NotImplemented是個值,單值,是NotImplementedType類的執行個體
  • NotImplementedError是類型,是異常,傳回type

運算符重載中的反向方法

  • 當正向方法傳回NotImplemented時調用反向方法

git伺服器搭建

gogs

軟體依賴

Mysql

安裝

useradd git
su - git
tar xf gogs*
cd gogs
mysql -uroot -p < scripts/mysql.sql           
grant all on gogs.* to 'gogs'@'%' identified by 'gogs';
flush privileges;           

配置檔案

mkdir -p custom/conf
cd custom/conf
touch app.ini           

啟動gogs

  1. ./gogs web
  2. 服務啟動
cp /home/git/gogs/scripts/init/centos/gogs /etc/init.d/
chmod +x /etc/init.d/gogs
chkconfig gogs on
service gogs start