Python中的包管理及插件化開發


第三十六、包管理

1、為什么使用包管理

目的是為了便於共享。為了更多項目調用使用,或者共享給別人,就需要打包,目的是為了復用。

Pypi(Python Package Index)公共的模塊存儲中心。https://pypi.python.org/pypi

2、主要工具

(1)distutils  官方標准庫,使用安裝腳本setup.py來構建、安裝包。

(2)setuptools 是替代distutils的增強版工具集,包含easy_install工具,使用ez_setup.py文件,支持egg格式的構建和安裝。

提供查詢,下載,安裝,構建,發布,管理等包管理功能。

(3)Pip

Pip是目前包管理的事實標准。

構建在setuptools之上,替代easy_insall的,同樣提供豐富的包管理功能。

 

(4)wheel

提供bdist_wheel作為setuptools的擴展命令,這個命令可以用來生成新打包格式wheel,

Pip開始提供了一個wheel子命令來安裝wheel包,必須先安裝wheel模塊,讓Python庫以二進制形式安裝,不需要再本地編譯。

3、使用setup.py打包

         首先創建一個setup.py文件,

內容:

from distutils.core import setup

setup(name='3',
      version='0.1.1',
      description = 'test',
      author='wcl',
      author_email='www.qq.com',
      packages = ['3']
)
#name:名字
# version 版本
# packages=[]打包列表
# packages = ['3']指定以后,就會把所有的非目錄子模塊打包。
# ['m','m.m1.m2.m3'] 逐級建立目錄,但是只是把m的所有非目錄子模塊打包。后面的也打包
#description 描述信息
#author 作者
#author_email作者郵件
#url 包的主頁,可以不寫

 

查詢幫助命令:Python setup.py cmd -help

4、build命令、編譯

創建一個build目錄

#Python setup.py build

 

在項目目錄下多了build目錄,有一個lib子目錄,lib下就是模塊m的目錄。

所有的.py 文件全部被復制了,但是子目錄沒有被復制。

 

構建了同樣的目錄結構,並只是拷貝了__init__.py 文件。

 

build 得到的文件,直接拷貝到其他項目就可以使用了。

 

打包的時候子包不要,模塊還是要帶上的。  setup.build。Packages后面寫的是包名,而不是模塊名。

5、install命令,安裝

Build后就可以install,直接運行

###Python setup.py install

如果沒有build,會先build編譯,然后安裝。

 

6、sdist命令,分發

Sdist命令:

####Python setup.py sdist

創建源代碼的分發包

產生一個dist目錄,里面生成一個帶版本號的壓縮包。

在其他地方解壓縮文件,里面有setup.py 就可以直接使用 Python setup.py install 安裝了,也可以 ##pip install xxxxxxxxxxx 直接使用pip安裝這個壓縮包。

 

 

制作window是下的分發包:Python setup.py bdist_wininst.

打包成rpm:Python setup.Py bdist_rpm

 

也可以將寫好的模塊發布到公共的pipy上,也可以搭建pypi私服。

模塊名前后:優先級。

7、wheel包                                 

安裝wheel依賴

## pip install wheel

from distutils.core import setup
from setuptools import setup
setup(name='3',
      version='0.1.1',
      description = 'test',
      author='wcl',
      author_email='www.qq.com',
      packages = ['3']
)

分發一下,元代碼打包成zip包。

 

 

 

 

 

 

 

 

第三十七、插件化開發

動態導入

運行時候,根據用戶需求(提供字符串),找到模塊的資源動態加載起來。

1、__import__()內建函數

__import__ (name,globals=None,locals = None,fromlist=(),level=0)

Name模塊名

Import語句本質上就是調用這個函數,但是不鼓勵其直接使用,使用importlib.import_module().

Sys = _import__(‘sys’)

 

M2模塊:

class A:
    def show(self):
        print('A')

 

 

M1:模塊

if __name__ == '__main__':
    mod = __import__('m2')
    cls = getattr(mod,'A')
    cls().show()

動態的調用

 

 

 

 

2、importlib.import_module()

 

class A:
    def show(self):
        print('A')

 

import importlib
def load(name:str,sep='.'):
    m,_,c = name.partition(sep)
    mod = importlib.import_module(m)
    cls = getattr(mod,c)
    return cls()

if __name__ == '__main__':
    a = load('m2.A')
    a.show()

 

A

插件化的核心代碼。

3、插件化編程技術

依賴的技術

反射:運行時候獲取類型的信息,可以動態維護類型數據。

動態import:推薦使用importlib模塊,實現動態import模塊的能力。

多線程:可以開啟一個線程。等待用戶輸入,從而加載指定名稱的模塊。

 

 

4、加載的時機

1)程序啟動時候。啟動時候掃描固定的目錄,加載插件。

2)程序運行時。程序運行中,接受用戶指令或請求,啟動相應的插件。

 

兩個方式各有利弊,如果插件過多,會導致程序啟動很慢,如果用戶使用時候在加載,如果插件太大或者依賴多,插件將會啟動慢。

所以先加載常用的,必須的插件,其他插件使用時候,發現需要,動態載入。

 

5、應用

軟件的設計不可能盡善盡美,或者在某些功能上,不可能做的太專業了,需要專業的客戶自己增強。

 

 

接口和插件的區別:

接口往往是暴露出來的功能。例如模塊提供的函數或方法,加載模塊后調用這些函數完成功能。接口也是一種規范,約定了必須實現的功能(必須提供某名稱的函數),但是不關心怎么實現這個功能。

 

插件是吧模塊加載到系統中,運行它,增強當前系統功能,或者提供系統不具備的功能,

往往插件技術應用在框架設計中,系統本身設計簡單化,輕量級,實現基本功能后,其他功能通過插件加入進來,方便擴展。

 

基礎知識補充的

1、__slots__

 

問題的引出

字典為了提升查詢效率,必須利用空間換時間。

一般來說一個對象,屬性多一點,都存儲在字典中便於查詢,問題不大。

對象數百萬個。字典占用率就有些大了。

 

只要slots定義的,就會阻止了實例的字典,沒有了字典,里面只能出現定義的屬性,沒有定義的一律不能使用。

class A:
    X = 1
    __slots__ = ('x','y')

    def __init__(self,x,y):
        self.x = x
        self.y = y

    def show(self):
        print(self.x,self.y)

a = A(1,2)
a.show()

print('A',A.__dict__)
# print(a.__dict__)  #字典被省略了
print(a.__slots__)

 

1 2

A {'y': <member 'y' of 'A' objects>, 'show': <function A.show at 0x000000972DAF4BF8>, '__init__': <function A.__init__ at 0x000000972DAF4D08>, '__doc__': None, 'x': <member 'x' of 'A' objects>, '__slots__': ('x', 'y'), 'X': 1, '__module__': '__main__'}

('x', 'y')

 

__slota__告訴解釋器,實例的屬性豆角什么,一般來說,既要節約內存,最好還是使用元組比較好。

 

而且實例不可以動態增加屬性,類直接增加,因為__slots__是針對實例的。

 

只能限制當前類的實例,不能繼承使用。除非子類里面自己也是定義了__slots__.

 

__slots__ = 后面寫元組比較好。

 

 

 

應用場景:

使用需要構建在數百萬以上的對象,且內存容量較為緊張,實例的屬性檢查、固定缺不用動態增加的場景。

 

2、未實現和未實現異常

1)NotImplemented  未實現。是個值,單值。

2)NotImplementedError    未實現異常。

3)只有raise才是無參數的構造。

print(type(NotImplemented))
print(type(NotImplementedError))
# raise NotImplemented   #不可以使用,顯示的是不屬於異常類
raise NotImplementedError

 

 

 

 

3、運算符重載中的反向方法

class A:
    def __init__(self,x):
        self.x = x

    def __add__(self, other):
        print(self,'add')
        return self.x + other.x

    def __iadd__(self, other):
        print(self,'iadd')
        return A(self.x + other.x)

    def __radd__(self, other):
        print(self,'radd')
        try:
            return self.x + other.x
        except AttributeError:
            try:
                x = int(other)
            except:
                x= 0
            return self.x + x

a = A(4)
# b = A(5)
1 + a

 

 

執行__radd__,實現1+a的方法。

字符串也是實現了__add__ 方法,不過默認處理不了和其他類型的加法,所以就返回NotImplemented。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM