天天看點

Python程式設計-綁定方法、軟體開發

本文介紹Python程式設計的綁定方法、軟體開發

一、綁定方法與非綁定方法

1.綁定方法

綁定給誰,誰來調用就自動将它本身當作第一個參數傳入

(1)綁定到類的方法:用classmethod裝飾器裝飾的方法。

為類量身定制

類.boud_method(),自動将類當作第一個參數傳入

(其實對象也可調用,但仍将類當作第一個參數傳入)

(2)綁定到對象的方法:沒有被任何裝飾器裝飾的方法。

為對象量身定制

對象.boud_method(),自動将對象當作第一個參數傳入

(屬于類的函數,類可以調用,但是必須按照函數的規則來,沒有自動傳值那麼一說)

2.非綁定方法

用staticmethod裝飾器裝飾的方法

不與類或對象綁定,類和對象都可以調用,但是沒有自動傳值那麼一說。就是一個普通工具而已

注意:與綁定到對象方法區分開,在類中直接定義的函數,沒有被任何裝飾器裝飾的,都是綁定到對象的方法,可不是普通函數,對象調用該方法會自動傳值,而staticmethod裝飾的方法,不管誰來調用,都沒有自動傳值一說。

3.staticmethod用法

statimethod不與類或對象綁定,誰都可以調用,沒有自動傳值效果,python為我們内置了函數staticmethod來把類中的函數定義成靜态方法。

import hashlib
import time
class MySQL:
    def __init__(self,host,port):
        self.id=self.create_id()
        self.host=host
        self.port=port
    @staticmethod
    def create_id(): #就是一個普通工具
        m=hashlib.md5(str(time.clock()).encode('utf-8'))
        return m.hexdigest()


print(MySQL.create_id) #<function MySQL.create_id at 0x0000000001E6B9D8> #檢視結果為普通函數
conn=MySQL('127.0.0.1',3306)
print(conn.create_id) #<function MySQL.create_id at 0x00000000026FB9D8> #檢視結果為普通函數
           

4.classmethod

classmehtod是給類用的,即綁定到類,類在使用時會将類本身當做參數傳給類方法的第一個參數(即便是對象來調用也會将類當作第一個參數傳入),python為我們内置了函數classmethod來把類中的函數定義成類方法。

#建立settings.py檔案,内容如下
HOST='127.0.0.1'
PORT=3306
DB_PATH=r'C:\Users\Administrator\PycharmProjects\test\面向對象程式設計\test1\db'
           
import settings
import hashlib
import time
class MySQL:
    def __init__(self,host,port):
        self.host=host
        self.port=port

    @classmethod
    def from_conf(cls):
        print(cls)
        return cls(settings.HOST,settings.PORT)

print(MySQL.from_conf) #<bound method MySQL.from_conf of <class '__main__.MySQL'>>
conn=MySQL.from_conf()

print(conn.host,conn.port)
conn.from_conf() #對象也可以調用,但是預設傳的第一個參數仍然是類
           

5.用法示例

class Foo:
    @classmethod
    def test(cls):
        print(cls)

print(Foo.test)
f=Foo()

Foo.test()
print(Foo)
f.test()
           
運作結果:
<bound method Foo.test of <class '__main__.Foo'>>
<class '__main__.Foo'>
<class '__main__.Foo'>
<class '__main__.Foo'>
           
class Foo:
    @staticmethod
    def test(x,y):
        print('test',x,y)

Foo.test(1,3)
f=Foo()
f.test(1,10)
           
運作結果:
test 1 3
test 1 10
           
class Foo:
    def test1(self):
        pass
    @classmethod
    def test2(cls):
        print(cls)
    @staticmethod
    def test3():
        pass

f=Foo()
print(f.test1)
print(Foo.test2)
print(Foo.test3)
print(f.test3)
           
運作結果:
<bound method Foo.test1 of <__main__.Foo object at 0x0000000001071C50>>
<bound method Foo.test2 of <class '__main__.Foo'>>
<function Foo.test3 at 0x0000000001073598>
<function Foo.test3 at 0x0000000001073598>
           
import settings
class MySQL:
    def __init__(self,host,port):
        self.host=host
        self.port=port
        print('conneting...')
		
    @classmethod
    def from_conf(cls):
        return cls(settings.HOST,settings.PORT) #MySQL('127.0.0.1',3306)
		
    def select(self): #綁定到對象的方法
        print(self)
        print('select function')

conn=MySQL('192.168.1.3',3306)
conn.select()

conn1=MySQL('192.168.1.3',3306)
conn2=MySQL.from_conf()
           
運作結果:
conneting...
<__main__.MySQL object at 0x00000000006D1DA0>
select function
conneting...
conneting...
           

ID随機數

import hashlib
import time
def create_id():
    m=hashlib.md5(str(time.clock()).encode('utf-8'))
    return m.hexdigest()

print(time.clock())
print(time.clock())
print(time.clock())

print(create_id())
print(create_id())
print(create_id())
print(create_id())
           
運作結果:
3.109938180648845e-07
4.074019016649987e-05
5.038099852651129e-05
5d14f5b3c90498d0b8237983016d86b8
6e4933ec7bb88b8a2b3ec28459687d96
70c803728dc6aa2cbed7e516a434beb8
7a69d8e021a2cb842a53f6bf50de82c6
           

ID随機數應用

import hashlib
import time
import settings
class MySQL:
    def __init__(self,host,port):
        self.id=self.create_id()
        self.host=host
        self.port=port
        print('conneting...')

    @staticmethod
    def create_id(): #非綁定方法,就是類中的普通工具包
        m=hashlib.md5(str(time.clock()).encode('utf-8'))
        return m.hexdigest()

    @classmethod
    def from_conf(cls):
        return cls(settings.HOST,settings.PORT) #MySQL('127.0.0.1',3306)
    def select(self): #綁定到對象的方法
        print(self)
        print('select function')

conn=MySQL('192.168.1.3',3306)
conn.select()

conn1=MySQL('192.168.1.3',3306)
conn1=MySQL.from_conf()
conn2=MySQL.from_conf()
conn3=MySQL.from_conf()
conn4=MySQL.from_conf()

print(conn1.id)
print(conn2.id)
print(conn3.id)
print(conn4.id)
           
運作結果:
conneting...
<__main__.MySQL object at 0x0000000000A6F400>
select function
conneting...
conneting...
conneting...
conneting...
conneting...
b70978d582704ddef008747f6a712abe
dcab93c2bcdb90f99149eb80ac002aaa
eb7a07a25ce10501e795454615809725
88491deb201c4a704a9e9376eb0dc787
           

statcimethod 與classmethod的差別

import settings
class MySQL:
    def __init__(self,host,port):
        self.host=host
        self.port=port
        # print('conneting...')
		
    @classmethod
    def from_conf(cls):
        return cls(settings.HOST,settings.PORT) #Mariadb('127.0.0.1',3306)

    @staticmethod
    def from_conf():
        return MySQL(settings.HOST, settings.PORT)  # MySQL('127.0.0.1',3306)

    def __str__(self):
        return '就不告訴你'
		
conn=MySQL.from_conf()
print(conn.host)

class Mariab(MySQL):
    def __str__(self):
        return 'host:%s port:%s' %(self.host,self.port)
    pass
conn1=Mariab.from_conf()
print(conn1)
           
運作結果:
127.0.0.1
就不告訴你
           
import settings
class MySQL:
    def __init__(self,host,port):
        self.host=host
        self.port=port
    @staticmethod
    def from_conf():
        return MySQL(settings.HOST,settings.PORT)

    # @classmethod
    # def from_conf(cls):
    #     return cls(settings.HOST,settings.PORT)

    def __str__(self):
        return '就不告訴你'

class Mariadb(MySQL):
    def __str__(self):
        return '主機:%s 端口:%s' %(self.host,self.port)

m=Mariadb.from_conf()
print(m) #我們的意圖是想觸發Mariadb.__str__,但是結果觸發了MySQL.__str__的執行,列印就不告訴你:
           

定義MySQL類

1)對象有id、host、port三個屬性

2)定義工具create_id,在執行個體化時為每個對象随機生成id,保證id唯一

3)提供兩種執行個體化方式,方式一:使用者傳入host和port 方式二:從配置檔案中讀取host和port進行執行個體化

4)為對象定制方法,save和get,save能自動将對象序列化到檔案中,檔案名為id号,檔案路徑為配置檔案中DB_PATH;get方法用來從檔案中反序列化出對象

import settings
import hashlib
import time
import random
import pickle
import os
class MySQL:
    def __init__(self,host,port):
        self.id=self.create_id()
        self.host=host
        self.port=port
    def save(self):
        file_path=r'%s%s%s' %(settings.DB_PATH,os.sep,self.id)
        pickle.dump(self,open(file_path,'wb'))

    def get(self):
        file_path = r'%s%s%s' % (settings.DB_PATH, os.sep, self.id)
        return pickle.load(open(file_path,'rb'))

    @staticmethod
    def create_id():
        m=hashlib.md5(str(time.clock()).encode('utf-8')) #檢視clock源碼注釋,指的是cpu真實時間,不要用time.time(),否則會出現id重複
        return m.hexdigest()
    @classmethod
    def from_conf(cls):
        print(cls)
        return cls(settings.HOST,settings.PORT)

print(MySQL.from_conf) #<bound method MySQL.from_conf of <class '__main__.MySQL'>>
conn=MySQL.from_conf()
print(conn.id)

print(conn.create_id())
print(MySQL.create_id())

print(conn.id)
conn.save()
obj=conn.get()
print(obj.id)
           

其他練習

import time

class Date:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day
    @staticmethod
    def now(): #用Date.now()的形式去産生執行個體,該執行個體用的是目前時間
        t=time.localtime() #擷取結構化的時間格式
        return Date(t.tm_year,t.tm_mon,t.tm_mday) #建立執行個體并且傳回
    @staticmethod
    def tomorrow():#用Date.tomorrow()的形式去産生執行個體,該執行個體用的是明天的時間
        t=time.localtime(time.time()+86400)
        return Date(t.tm_year,t.tm_mon,t.tm_mday)

a=Date('1987',11,27) #自己定義時間
b=Date.now() #采用目前時間
c=Date.tomorrow() #采用明天的時間

print(a.year,a.month,a.day)
print(b.year,b.month,b.day)
print(c.year,c.month,c.day)
           
運作結果:
1987 11 27
2017 6 13
2017 6 14
           
import time
class Date:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day
    @staticmethod
    def now():
        t=time.localtime()
        return Date(t.tm_year,t.tm_mon,t.tm_mday)

class EuroDate(Date):
    def __str__(self):
        return 'year:%s month:%s day:%s' %(self.year,self.month,self.day)

e=EuroDate.now()
print(e) #我們的意圖是想觸發EuroDate.__str__
           
運作結果:
<__main__.Date object at 0x0000000001091D68>
           

因為e就是用Date類産生的,是以根本不會觸發EuroDate.str,解決方法就是用classmethod

import time
class Date:
    def __init__(self,year,month,day):
        self.year=year
        self.month=month
        self.day=day
    # @staticmethod
    # def now():
    #     t=time.localtime()
    #     return Date(t.tm_year,t.tm_mon,t.tm_mday)

    @classmethod #改成類方法
    def now(cls):
        t=time.localtime()
        return cls(t.tm_year,t.tm_mon,t.tm_mday) #哪個類來調用,即用哪個類cls來執行個體化

class EuroDate(Date):
    def __str__(self):
        return 'year:%s month:%s day:%s' %(self.year,self.month,self.day)

e=EuroDate.now()
print(e) #我們的意圖是想觸發EuroDate.__str__,此時e就是由EuroDate産生的,是以會如我們所願
           
運作結果:
year:2017 month:6 day:13
           

二、面向對象的軟體開發

1.面向對象分析(object oriented analysis ,OOA)

軟體工程中的系統分析階段,要求分析員和使用者結合在一起,對使用者的需求做出精确的分析和明确的表述,從大的方面解析軟體系統應該做什麼,而不是怎麼去做。面向對象的分析要按照面向對象的概念和方法,在對任務的分析中,從客觀存在的事物和事物之間的關系,貴南出有關的對象(對象的‘特征’和‘技能’)以及對象之間的聯系,并将具有相同屬性和行為的對象用一個類class來辨別。

建立一個能反映這是工作情況的需求模型,此時的模型是粗略的。

2.面向對象設計(object oriented design,OOD)

根據面向對象分析階段形成的需求模型,對每一部分分别進行具體的設計。

首先是類的設計,類的設計可能包含多個層次(利用繼承與派生機制)。然後以這些類為基礎提出程式設計的思路和方法,包括對算法的設計。

在設計階段并不牽涉任何一門具體的計算機語言,而是用一種更通用的描述工具(如僞代碼或流程圖)來描述

3.面向對象程式設計(object oriented programming,OOP)

根據面向對象設計的結果,選擇一種計算機語言把它寫成程式,可以是python

4.面向對象測試(object oriented test,OOT)

在寫好程式後交給使用者使用前,必須對程式進行嚴格的測試,測試的目的是發現程式中的錯誤并修正它。

面向對的測試是用面向對象的方法進行測試,以類作為測試的基本單元。

5.面向對象維護(object oriendted soft maintenance,OOSM)

正如對任何産品都需要進行售後服務和維護一樣,軟體在使用時也會出現一些問題,或者軟體商想改進軟體的性能,這就需要修改程式。

由于使用了面向對象的方法開發程式,使用程式的維護比較容易。

因為對象的封裝性,修改一個對象對其他的對象影響很小,利用面向對象的方法維護程式,大大提高了軟體維護的效率,可擴充性高。

在面向對象方法中,最早發展的肯定是面向對象程式設計(OOP),那時OOA和OOD都還沒有發展起來,是以程式設計者為了寫出面向對象的程式,還必須深入到分析和設計領域,尤其是設計領域,那時的OOP實際上包含了現在的OOD和OOP兩個階段,這對程式設計者要求比較高,許多人感到很難掌握。

現在設計一個大的軟體,是嚴格按照面向對象軟體工程的5個階段進行的,這個5個階段的工作不是由一個人從頭到尾完成的,而是由不同的人分别完成,這樣OOP階段的任務就比較簡單了。程式編寫者隻需要根據OOd提出的思路,用面向對象語言編寫出程式既可。

在一個大型軟體開發過程中,OOP隻是很小的一個部分。

對于全棧開發的你來說,這五個階段都有了,對于簡單的問題,不必嚴格按照這個5個階段進行,往往由程式設計者按照面向對象的方法進行程式設計,包括類的設計和程式的設計。

三、python中關于OOP的常用術語

1.抽象/實作

抽象指對現實世界問題和實體的本質表現,行為和特征模組化,建立一個相關的子集,可以用于 繪程式結構,進而實作這種模型。抽象不僅包括這種模型的資料屬性,還定義了這些資料的接口。

對某種抽象的實作就是對此資料及與之相關接口的現實化(realization)。現實化這個過程對于客戶 程式應當是透明而且無關的。

2.封裝/接口

封裝描述了對資料/資訊進行隐藏的觀念,它對資料屬性提供接口和通路函數。通過任何用戶端直接對資料的通路,無視接口,與封裝性都是背道而馳的,除非程式員允許這些操作。作為實作的 一部分,用戶端根本就不需要知道在封裝之後,資料屬性是如何組織的。在Python中,所有的類屬性都是公開的,但名字可能被“混淆”了,以阻止未經授權的通路,但僅此而已,再沒有其他預防措施了。這就需要在設計時,對資料提供相應的接口,以免客戶程式通過不規範的操作來存取封裝的資料屬性。

注意:封裝絕不是等于“把不想讓别人看到、以後可能修改的東西用private隐藏起來”

真正的封裝是,經過深入的思考,做出良好的抽象,給出“完整且最小”的接口,并使得内部細節可以對外透明

(注意:對外透明的意思是,外部調用者可以順利的得到自己想要的任何功能,完全意識不到内部細節的存在)

3.合成

合成擴充了對類的 述,使得多個不同的類合成為一個大的類,來解決現實問題。合成 述了 一個異常複雜的系統,比如一個類由其它類組成,更小的元件也可能是其它的類,資料屬性及行為, 所有這些合在一起,彼此是“有一個”的關系。

4.派生/繼承/繼承結構

派生描述了子類衍生出新的特性,新類保留已存類類型中所有需要的資料和行為,但允許修改或者其它的自定義操作,都不會修改原類的定義。

繼承描述了子類屬性從祖先類繼承這樣一種方式

繼承結構表示多“代”派生,可以述成一個“族譜”,連續的子類,與祖先類都有關系。

5.泛化/特化

基于繼承

泛化表示所有子類與其父類及祖先類有一樣的特點。

特化描述所有子類的自定義,也就是,什麼屬性讓它與其祖先類不同。

6.多态與多态性

多态指的是同一種事物的多種狀态:水這種事物有多種不同的狀态:冰,水蒸氣

多态性的概念指出了對象如何通過他們共同的屬性和動作來操作及通路,而不需考慮他們具體的類。

冰,水蒸氣,都繼承于水,它們都有一個同名的方法就是變成雲,但是冰.變雲(),與水蒸氣.變雲()是截然不同的過程,雖然調用的方法都一樣

7.自省/反射

自省也稱作反射,這個性質展示了某對象是如何在運作期取得自身資訊的。如果傳一個對象給你,你可以查出它有什麼能力,這是一項強大的特性。如果Python不支援某種形式的自省功能,dir和type内建函數,将很難正常工作。還有那些特殊屬性,像__dict__,__name__及__doc__

四、問題總結

1.什麼樣的代碼才是面向對象?

從簡單來說,如果程式中的所有功能都是用類和對象來實作,那麼就是面向對象程式設計了。

2.函數式程式設計 和 面向對象 如何選擇?分别在什麼情況下使用?

須知:對于 C# 和 Java 程式員來說不存在這個問題,因為該兩門語言隻支援面向對象程式設計(不支援函數式程式設計)。而對于 Python 和 PHP 等語言卻同時支援兩種程式設計方式,且函數式程式設計能完成的操作,面向對象都可以實作;而面向對象的能完成的操作,函數式程式設計不行(函數式程式設計無法實作面向對象的封裝功能)。

是以,一般在Python開發中,全部使用面向對象 或 面向對象和函數式混合使用

面向對象的應用場景:

(1)多函數需使用共同的值,如:資料庫的增、删、改、查操作都需要連接配接資料庫字元串、主機名、使用者名和密碼

class SqlHelper:

    def __init__(self, host, user, pwd):

        self.host = host
        self.user = user
        self.pwd = pwd

    def 增(self):
        # 使用主機名、使用者名、密碼(self.host 、self.user 、self.pwd)打開資料庫連接配接
        # do something
        # 關閉資料庫連接配接

    def 删(self):
        # 使用主機名、使用者名、密碼(self.host 、self.user 、self.pwd)打開資料庫連接配接
        # do something
        # 關閉資料庫連接配接

    def 改(self):
        # 使用主機名、使用者名、密碼(self.host 、self.user 、self.pwd)打開資料庫連接配接
        # do something
        # 關閉資料庫連接配接

    def 查(self):
    # 使用主機名、使用者名、密碼(self.host 、self.user 、self.pwd)打開資料庫連接配接
        # do something
        # 關閉資料庫連接配接# do something
           

(2)需要建立多個事物,每個事物屬性個數相同,但是值的需求

如:張三、李四、楊五,他們都有姓名、年齡、血型,但其都是不相同。即:屬性個數相同,但值不相同

class Person:

    def __init__(self, name ,age ,blood_type):

        self.name = name
        self.age = age
        self.blood_type = blood_type


    def detail(self):
        temp = "i am %s, age %s , blood type %s " % (self.name, self.age, self.blood_type)
        print temp

zhangsan = Person('張三', 18, 'A')
lisi = Person('李四', 73, 'AB')
yangwu = Person('楊五', 84, 'A')
           

3.類和對象在記憶體中是如何儲存?

類以及類中的方法在記憶體中隻有一份,而根據類建立的每一個對象都在記憶體中需要存一份,大緻如下圖:

Python程式設計-綁定方法、軟體開發

如上圖所示,根據類建立對象時,對象中除了封裝name和age的值之外,還會儲存一個類對象指針,該值指向目前對象的類。

當通過obj1執行 【方法一】 時,過程如下:

  • 根據目前對象中的類對象指針找到類中的方法
  • 将對象obj1當作參數傳給方法的第一個參數self

繼續閱讀