嵌入式與人工智能培訓——python總結(二)——模塊、庫、OOP

在嵌入式智能小車項目中,涉及到很多模塊的控制,比如小車的移動方式,避障以及循跡導航等,這就涉及到更高級的編程策略,需要用到模塊化編程,面向對象思想以及多線程運行方式。

1,模塊化編程相關內容:

在python中,將一些常用變量,方法等定義到.py文件中,之後在編程的時候通過import該文件,進而調用該文件中的方法的方式即爲模塊化編程,而這個被調用文件即爲模塊。python中有很多標準庫,可以直接拿來用,不用自己定義,比如sys,math,最爲出色的就是機器學習相關的一些庫,如scikit-learn,pandas等

在我們自定的模塊調用中,需要將待調用的文件放到當前文件目錄或者python的系統搜索路徑(import sys; print sys.path獲取)中,下面是對於標準庫和自定義模塊的示例:

代碼文件 測試輸出

#!/usr/bin/python3

# 文件名: using_sys.py

import sys    #標準庫

print('命令行參數如下:')

for i in sys.argv:

    print(i)

print('\n\nPython 路徑爲:', sys.path, '\n')

$python using_sys.py param1 param2

-->output:

命令行參數如下:

using_sys.py

param1

param2

#!/usr/bin/python3

# 文件名: support.py

 #自定義模塊庫

def print_func( par ):

    print ("Hello : ", par)

    return

#!/usr/bin/python3

# test.py 調用模塊

import support # 導入模塊

# 調用模塊裏包含的函數

support.print_func("Runoob")

$python test.py

-->output

Hello: Runoob

直接import modulename則可通過modulename.function()方式調用模塊函數,或者可以通過from .... import僅調用我們需要的模塊,比如斐波那契(fibonacci)數列模塊名爲fibo,包含函數fib(n) & fib2(n),用法1:import fibo ;  fibo.fib(20) 調用  用法2:from fibo import fib   ;   fib(20)調用(from modulename import *則是導入該模塊中的所有函數)

  • 特點:

__name__屬性:模塊被引入時,若要模塊中的某一程序塊不執行,可以用__name__屬性來使該程序塊僅在該模塊自身運行時執行。

dir()函數:內置的函數 dir() 可以找到模塊內定義的所有名稱。以一個字符串列表的形式返回: >>>dir(fibo)   -->output: ['__name__', 'fib', 'fib2']

  • 包:包是一種管理 Python 模塊命名空間的形式,採用"點模塊名稱"

比如一個模塊的名稱是 A.B, 那麼他表示一個包 A中的子模塊 B

比如下面對音頻處理的包結構:

sound/ 頂層包

            __init__.py

           formats/

                       __init__.py

                       wavread.py

                       wavwrite.py 

                        。。。

初始化sound包

文件轉換格式子包

 

 

 

 

           effects/

                       __init__.py

                      echo.py

                      。。。

聲音效果子包

 

 

 

這樣我們就擁有了幾個功能耦合性較高的模塊組合,如我們可以採用import sound.effects.echo方式來導入echo模塊

2,python面向對象編程:顧名思義就是面向實體對象的編程,把需要用到的方法等和一個個對象實例——類結合起來

在python中類的定義以及參考示例如下表所示:

語法格式如下:

class ClassName:

    <statement-1>

    . . .

    <statement-N>

類對象支持兩種操作:屬性引用和實例化

屬性引用標準語法: object.name

簡單示例:

#!/usr/bin/python3

#類定義

class people:

#定義基本屬性

    name = ''

    age = 0

#定義私有屬性,私有屬性在類外部無法直接進行訪問

    __weight = 0

#定義構造方法

    def __init__(self,n,a,w):

        self.name = n

        self.age = a

        self.__weight = w

    def speak(self):

        print("%s 說: 我 %d 歲。" %(self.name,self.age))

# 實例化類

p = people('runoob',10,30)

p.speak()  #輸出:runoob 說: 我 10 歲

---類有一個名爲 __init__() 的特殊方法(構造方法)該方法在類實例化時會自動調用

---類的方法與普通的函數只有一個特別的區別——它們必須有一個額外的第一個參數名稱, 按照慣例它的名稱是 self

  • python面向對象編程滿足一般面向對象編程的特點:繼承,方法重寫等,具體示例及說明如下表:

#!/usr/bin/python3

#類定義

class people:

#定義基本屬性

    name = ''

    age = 0

    #定義私有屬性,私有屬性在類外部無法直接進行訪問

    __weight = 0

    #定義構造方法

    def __init__(self,n,a,w):

        self.name = n

        self.age = a

        self.__weight = w

    def speak(self):

        print("%s 說: 我 %d 歲。" %(self.name,self.age))

#單繼承示例

class student(people):

    grade = ''

    def __init__(self,n,a,w,g): #調用父類的構函

        people.__init__(self,n,a,w)

        self.grade = g

    def speak(self):   #覆寫父類的方法

        print("%s 說: 我 %d 歲了,我在讀 %d 年級"%(self.name,self.age,self.grade))

s = student('ken',10,60,3)

s.speak()  #輸出:ken 說: 我 10 歲了,我在讀 3 年級

類的繼承格式如下:

class DerivedClassName(BaseClassName1):

    <statement-1>

    . . .

    <statement-N>

BaseClassName(示例中的基類名)必須與派生類定義在一個作用域內。除了類,還可以用表達式,基類定義在另一個模塊中時這一點非常有用:

class DerivedClassName(modname.BaseClassName):

 

 

PS:注意圓括號中基類的順序,若是基類中有相同的方法名,而在子類使用時未指定,python從左至右搜索 即方法在子類中未找到時,從左到右查找基類中是否包含方法

在上面示例中再加入一個類,演示多繼承:

#另一個類,多重繼承之前的準備

class speaker():

    topic = ''

    name = ''

    def __init__(self,n,t):

        self.name = n

        self.topic = t

    def speak(self):

        print("我叫 %s,我是一個演說家,我演講的主題是 %s"%(self.name,self.topic))

#多重繼承

class sample(speaker,student):

    a =''

    def __init__(self,n,a,w,g,t):

        student.__init__(self,n,a,w,g)         

        speaker.__init__(self,n,t)

test = sample("Tim",25,80,4,"Python")

test.speak()

Python同樣有限的支持多繼承形式:

class DerivedClassName(Base1, Base2, Base3):

    <statement-1>

    . . .

    <statement-N>

!!圓括號中父類的順序,若是父類中有相同的方法名,而在子類使用時未指定,python從左至右搜索 即方法在子類中未找到時,從左到右查找父類中是否包含方法

 

#方法名同,默認調用的是在括號中排前地父類的方法:

---左邊程序輸出:

我叫 Tim,我是一個演說家,我演講的主題是 Python

#!/usr/bin/python3

class Parent:

# 定義父類

    def myMethod(self):

        print ('調用父類方法')

class Child(Parent): # 定義子類

    def myMethod(self):

        print ('調用子類方法')

c = Child() # 子類實例

c.myMethod() # 子類調用重寫方法 super(Child,c).myMethod() #用子類對象調用父類已被覆蓋的方法

如果父類方法不能滿足需求,就需要方法重寫,

示例如左邊示例所示

輸出結果:

調用子類方法

調用父類方法

  • python類還有一些專有方法,相當於C/C++中的運算符函數,有:
專有方法 運算符重載示例
  • __init__ : 構造函數,在生成對象時調用
  • __del__ : 析構函數,釋放對象時使用
  • __repr__ : 打印,轉換
  • __setitem__ : 按照索引賦值
  • __getitem__: 按照索引獲取值
  • __len__: 獲得長度
  • __cmp__: 比較運算
  • __call__: 函數調用
  • __add__: 加運算
  • __sub__: 減運算
  • __mul__: 乘運算
  • __truediv__: 除運算
  • __mod__: 求餘運算
  • __pow__: 乘方

#!/usr/bin/python3

class Vector:

    def __init__(self, a, b):

        self.a = a

        self.b = b

    def __str__(self):

        return 'Vector (%d, %d)' % (self.a, self.b)

    def __add__(self,other):

        return Vector(self.a + other.a, self.b + other.b)

v1 = Vector(2,10)

v2 = Vector(5,-2)

print (v1 + v2)   

輸出:Vector(7,8)

3,這一篇最後一部分是關於python的多線程總結:

Python的標準庫提供了兩個模塊:_threadthreading_thread是低級模塊,threading是高級模塊,對_thread進行了封裝。多數情況下,我們只需要用threading模塊,啓動線程就是傳入函數,創建Thread實例,調用start運行,下面是一個示例:

import time, threading

# 新線程執行的代碼:
def loop():
    print('thread %s is running...' % threading.current_thread().name)
    n = 0
    while n < 5:
        n = n + 1
        print('thread %s >>> %s' % (threading.current_thread().name, n))
        time.sleep(1)
    print('thread %s ended.' % threading.current_thread().name)

print('thread %s is running...' % threading.current_thread().name)
t = threading.Thread(target=loop, name='LoopThread')
t.start()
t.join()
print('thread %s ended.' % threading.current_thread().name)

輸出:
thread MainThread is running...
thread LoopThread is running...
thread LoopThread >>> 1
thread LoopThread >>> 2
thread LoopThread >>> 3
thread LoopThread >>> 4
thread LoopThread >>> 5
thread LoopThread ended.
thread MainThread ended.

import time, threading

balance = 0

lock = threading.Lock()

def change_it(n):
    # 先存後取,結果應該爲0:
    global balance
    balance = balance + n
    balance = balance - n

def run_thread(n):
    for i in range(100000):
        # 先要獲取鎖:
        lock.acquire()
        try:
            # 放心地改吧:
            change_it(n)
        finally:
            # 改完了一定要釋放鎖:
            lock.release()
t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)

多線程中,所有變量都由所有線程共享,存在一個危險在於多個線程同時改一個變量,把內容給改亂了,這裏通過加鎖來防止髒數據的出現

在智能小車項目中最常使用的內容是面向對象得線程編程,涉及到模塊的線程啓動,暫停等操作,還需要注意一個無限循環的線程問題:

#!/usr/bin/env python
# coding: utf-8

import threading
import time

#用於線程的恢復和暫停
class Job(threading.Thread):
	def __init__(self, *args, **kwargs):
		super(Job, self).__init__(*args, **kwargs)		#調用父類的構造函數
		self.__flag = threading.Event()     	# 用於暫停線程的標識
		self.__flag.set()       				# 設置爲True
		self.__running = threading.Event()      # 用於停止線程的標識
		self.__running.set()      # 將running設置爲True
	def run(self):
		while self.__running.isSet():			#檢測線程是否運行
			self.__flag.wait()      # 爲True時立即返回, 爲False時阻塞直到內部的標識位爲True後返回
			localtime = time.asctime(time.localtime(time.time()))
			print localtime
			#print time.time()
			time.sleep(1)
	def pause(self):	
		self.__flag.clear()     # 設置爲False, 讓線程阻塞

	def resume(self):	
		self.__flag.set()    # 設置爲True, 讓線程停止阻塞

	def stop(self):
		self.__flag.set()       # 將線程從暫停狀態恢復, 如何已經暫停的話
		self.__running.clear()        # 設置爲False    
a = Job()
a.start()
time.sleep(3)
a.pause()
time.sleep(3)
a.resume()
time.sleep(3)
a.pause()
time.sleep(2)
a.stop()

 

左邊是控制線程啓動暫停地demo
#!/usr/bin/bash
#coding=utf-8

import RPi.GPIO as GPIO
import threading
import time
import manual_motor as motor

RFLAG = 0
def route():
    while RFLAG:
        if GPIO.input(left_in) == 1 and GPIO.input(right_in) == 1:
            motor.state_handler(1,20)  ##line
        elif GPIO.input(left_in) == 0 and GPIO.input(right_in) == 0:
            motor.state_handler(1,20)  ##line
        elif GPIO.input(left_in) == 1 and GPIO.input(right_in) == 0:
            motor.state_handler(-2,20)  ##left turn
        elif GPIO.input(left_in) == 0 and GPIO.input(right_in) == 1:
            motor.state_handler(2,20)  ##right turn
        else:
            motor.state_handler(0,0)  ##stop
    
#用於線程的恢復和暫停
class Route(threading.Thread):
    def __init__(self, *args, **kwargs):
	super(Route, self).__init__(*args, **kwargs)#調用父類的構造函數
	self.__flag = threading.Event()     	# 用於暫停線程的標識
	self.__flag.set()       		# 設置爲True
	self.__running = threading.Event()      # 用於停止線程的標識
	self.__running.set()   # running設True
        self.__func = threading.Thread() 
    def run(self):
	while self.__running.isSet():		#檢測線程是否運行
		self.__flag.wait()
                global RFLAG
                RFLAG = 1
                route()
		time.sleep(0.1)

    def pause(self):
        global RFLAG
        RFLAG = 0
	self.__flag.clear()  # False-->block

    def resume(self):
        global RFLAG
        RFLAG = 1
	self.__flag.set()  # stop been blocked

    def stop(self):
        global RFLAG
        RFLAG = 0
	self.__flag.set()   # reset-->recover
	self.__running.clear()  # set False    

 

左邊是實際小車控制發動機轉動的無限循環線程的控制方式,通過全局變量來控制線程調用函數得持續執行

python部分除了後面需要小車上的樹莓派主板與遠程控制桌面連接需要用到一點網絡編程得東西,基本上涉及到的內容就以上兩篇博客提到的內容了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章