1. 程式人生 > >python學習點滴記錄-Day08

python學習點滴記錄-Day08

sta 試圖 typeerror 閱讀 txt lan 另有 dex 訪問權限

本次課大綱

  • 接口與歸一化設計

  • 多態與多態性

  • 封裝

  • 靜態屬性property

  • 面向對象高級

  • 異常處理

  • 網絡編程


接口與歸一化設計

1.什麽是接口

技術分享
=================第一部分:Java 語言中的接口很好的展現了接口的含義: IAnimal.java
/*
* Java的Interface接口的特征:
* 1)是一組功能的集合,而不是一個功能
* 2)接口的功能用於交互,所有的功能都是public,即別的對象可操作
* 3)接口只定義函數,但不涉及函數實現
* 4)這些功能是相關的,都是動物相關的功能,但光合作用就不適宜放到IAnimal裏面了 */
package com.oo.demo; public interface IAnimal { public void eat(); public void run(); public void sleep(); public void speak(); } =================第二部分:Pig.java:豬”的類設計,實現了IAnnimal接口 package com.oo.demo; public class Pig implements IAnimal{ //如下每個函數都需要詳細實現 public void eat(){ System.out.println(
"Pig like to eat grass"); } public void run(){ System.out.println("Pig run: front legs, back legs"); } public void sleep(){ System.out.println("Pig sleep 16 hours every day"); } public void speak(){ System.out.println("Pig can not speak"); } } =================第三部分:Person2.java
/* *實現了IAnimal的“人”,有幾點說明一下: * 1)同樣都實現了IAnimal的接口,但“人”和“豬”的實現不一樣,為了避免太多代碼導致影響閱讀,這裏的代碼簡化成一行,但輸出的內容不一樣,實際項目中同一接口的同一功能點,不同的類實現完全不一樣 * 2)這裏同樣是“人”這個類,但和前面介紹類時給的類“Person”完全不一樣,這是因為同樣的邏輯概念,在不同的應用場景下,具備的屬性和功能是完全不一樣的 */ package com.oo.demo; public class Person2 implements IAnimal { public void eat(){ System.out.println("Person like to eat meat"); } public void run(){ System.out.println("Person run: left leg, right leg"); } public void sleep(){ System.out.println("Person sleep 8 hours every dat"); } public void speak(){ System.out.println("Hellow world, I am a person"); } } =================第四部分:Tester03.java package com.oo.demo; public class Tester03 { public static void main(String[] args) { System.out.println("===This is a person==="); IAnimal person = new Person2(); person.eat(); person.run(); person.sleep(); person.speak(); System.out.println("\n===This is a pig==="); IAnimal pig = new Pig(); pig.eat(); pig.run(); pig.sleep(); pig.speak(); } } java中的interface
java中的interface接口

例:hi boy,給我開個查詢接口。。。此時的接口指的是:自己提供給使用者來調用自己功能的方式\方法\入口

2. 為何要用接口

接口提取了一群類共同的函數,可以把接口當做一個函數的集合。

然後讓子類去實現接口中的函數。

這麽做的意義在於歸一化,什麽叫歸一化,就是只要是基於同一個接口實現的類,那麽所有的這些類產生的對象在使用時,從用法上來說都一樣。

歸一化的好處在於:

1. 歸一化讓使用者無需關心對象的類是什麽,只需要的知道這些對象都具備某些功能就可以了,這極大地降低了使用者的使用難度。

2. 歸一化使得高層的外部使用者可以不加區分的處理所有接口兼容的對象集合

2.1:就好象linux的泛文件概念一樣,所有東西都可以當文件處理,不必關心它是內存、磁盤、網絡還是屏幕(當然,對底層設計者,當然也可以區分出“字符設備”和“塊設備”,然後做出針對性的設計:細致到什麽程度,視需求而定)。

2.2:再比如:我們有一個汽車接口,裏面定義了汽車所有的功能,然後由本田汽車的類,奧迪汽車的類,大眾汽車的類,他們都實現了汽車接口,這樣就好辦了,大家只需要學會了怎麽開汽車,那麽無論是本田,還是奧迪,還是大眾我們都會開了,開的時候根本無需關心我開的是哪一類車,操作手法(函數調用)都一樣

3. 模仿interface

在python中根本就沒有一個叫做interface的關鍵字,如果非要去模仿接口的概念

可以借助第三方模塊:

http://pypi.python.org/pypi/zope.interface

twisted的twisted\internet\interface.py裏使用zope.interface

文檔https://zopeinterface.readthedocs.io/en/latest/

設計模式:https://github.com/faif/python-patterns

也可以使用繼承:

繼承的兩種用途

一:繼承基類的方法,並且做出自己的改變或者擴展(代碼重用):實踐中,繼承的這種用途意義並不很大,甚至常常是有害的。因為它使得子類與基類出現強耦合。

二:聲明某個子類兼容於某基類,定義一個接口類(模仿java的Interface),接口類中定義了一些接口名(就是函數名)且並未實現接口的功能,子類繼承接口類,並且實現接口中的功能

技術分享
class Interface:#定義接口Interface類來模仿接口的概念,python中壓根就沒有interface關鍵字來定義一個接口。
    def read(self): #定接口函數read
        pass

    def write(self): #定義接口函數write
        pass


class Txt(Interface): #文本,具體實現read和write
    def read(self):
        print(文本數據的讀取方法)

    def write(self):
        print(文本數據的讀取方法)

class Sata(Interface): #磁盤,具體實現read和write
    def read(self):
        print(硬盤數據的讀取方法)

    def write(self):
        print(硬盤數據的讀取方法)

class Process(Interface):
    def read(self):
        print(進程數據的讀取方法)

    def write(self):
        print(進程數據的讀取方法)
python繼承模擬接口

上面的代碼只是看起來像接口,其實並沒有起到接口的作用,子類完全可以不用去實現接口 ,這就用到了抽象類


多臺與多態性(待補充)


封裝

一、引子

封裝的字面意思就是隱藏起來

二、在python中如何實現隱藏

在python中用雙下劃線開頭的方式將屬性隱藏起來(設置成私有的)

#其實這僅僅這是一種變形操作
#類中所有雙下劃線開頭的名稱如__x都會自動變形成:_類名__x的形式:

class A:
    __N=0 #類的數據屬性就應該是共享的,但是語法上是可以把類的數據屬性設置成私有的如__N,會變形為_A__N
    def __init__(self):
        self.__X=10 #變形為self._A__X
    def __foo(self): #變形為_A__foo
        print(from A)
    def bar(self):
        self.__foo() #只有在類內部才可以通過__foo的形式訪問到.

#A._A__N是可以訪問到的,即這種操作並不是嚴格意義上的限制外部訪問,僅僅只是一種語法意義上的變形

這種自動變形的特點:

1.類中定義的__x只能在內部使用,如self.__x,引用的就是變形的結果

2.這種變形其實正是針對外部的變形,在外部是無法通過__x這個名字訪問到的。

3.在子類定義的__x不會覆蓋在父類定義的__x,因為子類中變形成了:_子類名__x,而父類中變形成了:_父類名__x,即雙下滑線開頭的屬性在繼承給子類時,子類是無法覆蓋的。

這種變形需要註意的問題是:

1.這種機制也並沒有真正意義上限制我們從外部直接訪問屬性,知道了類名和屬性名就可以拼出名字:_類名__屬性,然後就可以訪問了,如a._A__N

2.變形的過程只在類的定義是發生一次,在定義後的賦值操作,不會變形

技術分享

3.在繼承中,父類如果不想讓子類覆蓋自己的方法,可以將方法定義為私有的

技術分享
#正常情況
>>> class A:
...     def fa(self):
...         print(from A)
...     def test(self):
...         self.fa()
... 
>>> class B(A):
...     def fa(self):
...         print(from B)
... 
>>> b=B()
>>> b.test()
from B
 

#把fa定義成私有的,即__fa
>>> class A:
...     def __fa(self): #在定義時就變形為_A__fa
...         print(from A)
...     def test(self):
...         self.__fa() #只會與自己所在的類為準,即調用_A__fa
... 
>>> class B(A):
...     def __fa(self):
...         print(from B)
... 
>>> b=B()
>>> b.test()
from A
View Code

三、封裝不是簡單的隱藏

1:封裝數據:將數據隱藏起來這不是目的。隱藏起來然後對外提供操作該數據的接口,然後我們可以在接口附加上對該數據操作的限制,以此完成對數據屬性操作的嚴格控制。

技術分享
class Teacher:
    def __init__(self,name,age):
        self.__name=name
        self.__age=age

    def tell_info(self):
        print(姓名:%s,年齡:%s %(self.__name,self.__age))
    def set_info(self,name,age):
        if not isinstance(name,str):
            raise TypeError(姓名必須是字符串類型)
        if not isinstance(age,int):
            raise TypeError(年齡必須是整型)
        self.__name=name
        self.__age=age

t=Teacher(lele,18)
t.tell_info()

t.set_info(lele,19)
t.tell_info()
View Code

2:封裝方法:目的是隔離復雜度

封裝方法舉例:

1. 你的身體沒有一處不體現著封裝的概念:你的身體把膀胱尿道等等這些尿的功能隱藏了起來,然後為你提供一個尿的接口就可以了(接口就是你的。。。,),你總不能把膀胱掛在身體外面,上廁所的時候就跟別人炫耀:hi,man,你瞅我的膀胱,看看我是怎麽尿的。

2. 電視機本身是一個黑盒子,隱藏了所有細節,但是一定會對外提供了一堆按鈕,這些按鈕也正是接口的概念,所以說,封裝並不是單純意義的隱藏!!!

3. 快門就是傻瓜相機為傻瓜們提供的方法,該方法將內部復雜的照相功能都隱藏起來了

提示:在編程語言裏,對外提供的接口(接口可理解為了一個入口),可以是函數,稱為接口函數,這與接口的概念還不一樣,接口代表一組接口函數的集合體。

技術分享
#取款是功能,而這個功能有很多功能組成:插卡、密碼認證、輸入金額、打印賬單、取錢
#對使用者來說,只需要知道取款這個功能即可,其余功能我們都可以隱藏起來,很明顯這麽做
#隔離了復雜度,同時也提升了安全性

class ATM:
    def __card(self):
        print(插卡)
    def __auth(self):
        print(用戶認證)
    def __input(self):
        print(輸入取款金額)
    def __print_bill(self):
        print(打印賬單)
    def __take_money(self):
        print(取款)

    def withdraw(self):
        self.__card()
        self.__auth()
        self.__input()
        self.__print_bill()
        self.__take_money()

a=ATM()
a.withdraw()

隔離復雜度的例子
View Code

3: 了解

python並不會真的阻止你訪問私有的屬性,模塊也遵循這種約定,如果模塊名以單下劃線開頭,那麽from module import *時不能被導入,但是你from module import _private_module依然是可以導入的

其實很多時候你去調用一個模塊的功能時會遇到單下劃線開頭的(socket._socket,sys._home,sys._clear_type_cache),這些都是私有的,原則上是供內部調用的,作為外部的你,一意孤行也是可以用的,只不過顯得稍微傻逼一點點

python要想與其他編程語言一樣,嚴格控制屬性的訪問權限,只能借助內置方法如__getattr__,詳見面向對象進階

四 特性(property)

什麽是特性property

property是一種特殊的屬性,訪問它時會執行一段功能(函數)然後返回值

就是將函數屬性的調用方式偽裝成數據屬性的調用方式

例一:BMI指數(bmi是計算而來的,但很明顯它聽起來像是一個屬性而非方法,如果我們將其做成一個屬性,更便於理解)

成人的BMI數值: 過輕:低於18.5 正常:18.5-23.9 過重:24-27 肥胖:28-32 非常肥胖, 高於32   體質指數(BMI)=體重(kg)÷身高^2(m)   EX:70kg÷(1.75×1.75)=22.86 技術分享
class People:
    def __init__(self,name,weight,height):
        self.name=name
        self.weight=weight
        self.height=height
    @property
    def bmi(self):
        return self.weight / (self.height**2)

p1=People(egon,75,1.85)
print(p1.bmi)
View Code

為什麽要用property

將一個類的函數定義成特性以後,對象再去使用的時候obj.name,根本無法察覺自己的name是執行了一個函數然後計算出來的,這種特性的使用方式遵循了統一訪問的原則

除此之外,看下

ps:面向對象的封裝有三種方式:
【public】
這種其實就是不封裝,是對外公開的
【protected】
這種封裝方式對外不公開,但對朋友(friend)或者子類(形象的說法是“兒子”,但我不知道為什麽大家 不說“女兒”,就像“parent”本來是“父母”的意思,但中文都是叫“父類”)公開
【private】
這種封裝對誰都不公開

python並沒有在語法上把它們三個內建到自己的class機制中,在C++裏一般會將所有的所有的數據都設置為私有的,然後提供set和get方法(接口)去設置和獲取,在python中通過property方法可以實現

#訪問,設置,刪除(了解)

技術分享
class Foo:
    def __init__(self,val):
        self.__NAME=val #將所有的數據屬性都隱藏起來

    @property
    def name(self):
        return self.__NAME #obj.name訪問的是self.__NAME(這也是真實值的存放位置)

    @name.setter
    def name(self,value):
        if not isinstance(value,str):  #在設定值之前進行類型檢查
            raise TypeError(%s must be str %value)
        self.__NAME=value #通過類型檢查後,將值value存放到真實的位置self.__NAME

    @name.deleter
    def name(self):
        raise TypeError(Can not delete)

f=Foo(egon)
print(f.name)
# f.name=10 #拋出異常‘TypeError: 10 must be str‘
del f.name #拋出異常‘TypeError: Can not delete‘
View Code

面向對象高級

反射、item、打印對象信息、析構方法 待補充


異常處理

一 什麽是異常

異常就是程序運行時發生錯誤的信號(在程序出現錯誤時,則會產生一個異常,若程序沒有處理它,則會拋出該異常,程序的運行也隨之終止),在python中,錯誤觸發的異常如下

技術分享

錯誤分成兩種

技術分享
#語法錯誤示範一
if
#語法錯誤示範二
def test:
    pass
#語法錯誤示範三
class Foo
    pass
#語法錯誤示範四
print(haha

1.語法錯誤(這種錯誤,根本過不了python解釋器的語法檢測,必須在程序執行前就改正)
1.語法錯誤(這種錯誤,根本過不了python解釋器的語法檢測,必須在程序執行前就改正) 技術分享
#TypeError:int類型不可叠代
for i in 3:
    pass
#ValueError
num=input(">>: ") #輸入hello
int(num)

#NameError
aaa

#IndexError
l=[egon,aa]
l[3]

#KeyError
dic={name:egon}
dic[age]

#AttributeError
class Foo:pass
Foo.x

#ZeroDivisionError:無法完成計算
res1=1/0
res2=1+str

2.邏輯錯誤
邏輯錯誤

二 異常的種類

在python中不同的異常可以用不同的類型(python中統一了類與類型,類型即類)去標識,一個異常標識一種錯誤

常見異常

技術分享
AttributeError 試圖訪問一個對象沒有的樹形,比如foo.x,但是foo沒有屬性x
IOError 輸入/輸出異常;基本上是無法打開文件
ImportError 無法引入模塊或包;基本上是路徑問題或名稱錯誤
IndentationError 語法錯誤(的子類) ;代碼沒有正確對齊
IndexError 下標索引超出序列邊界,比如當x只有三個元素,卻試圖訪問x[5]
KeyError 試圖訪問字典裏不存在的鍵
KeyboardInterrupt Ctrl+C被按下
NameError 使用一個還未被賦予對象的變量
SyntaxError Python代碼非法,代碼不能編譯(個人認為這是語法錯誤,寫錯了)
TypeError 傳入對象類型與要求的不符合
UnboundLocalError 試圖訪問一個還未被設置的局部變量,基本上是由於另有一個同名的全局變量,
導致你以為正在訪問它
ValueError 傳入一個調用者不期望的值,即使值的類型是正確的

常用異常
View Code 技術分享
ArithmeticError
AssertionError
AttributeError
BaseException
BufferError
BytesWarning
DeprecationWarning
EnvironmentError
EOFError
Exception
FloatingPointError
FutureWarning
GeneratorExit
ImportError
ImportWarning
IndentationError
IndexError
IOError
KeyboardInterrupt
KeyError
LookupError
MemoryError
NameError
NotImplementedError
OSError
OverflowError
PendingDeprecationWarning
ReferenceError
RuntimeError
RuntimeWarning
StandardError
StopIteration
SyntaxError
SyntaxWarning
SystemError
SystemExit
TabError
TypeError
UnboundLocalError
UnicodeDecodeError
UnicodeEncodeError
UnicodeError
UnicodeTranslateError
UnicodeWarning
UserWarning
ValueError
Warning
ZeroDivisionError

更多異常
View Code

三 異常處理

為了保證程序的健壯性與容錯性,即在遇到錯誤時程序不會崩潰,我們需要對異常進行處理,

如果錯誤發生的條件是可預知的,我們需要用if進行處理:在錯誤發生之前進行預防

技術分享
AGE=10
while True:
    age=input(>>: ).strip()
    if age.isdigit(): #只有在age為字符串形式的整數時,下列代碼才不會出錯,該條件是可預知的
        age=int(age)
        if age == AGE:
            print(you got it)
            break
View Code

如果錯誤發生的條件是不可預知的,則需要用到try...except:在錯誤發生之後進行處理

技術分享
#基本語法為
try:
    被檢測的代碼塊
except 異常類型:
    try中一旦檢測到異常,就執行這個位置的邏輯
#舉例
try:
    f=open(a.txt)
    g=(line.strip() for line in f)
    print(next(g))
    print(next(g))
    print(next(g))
    print(next(g))
    print(next(g))
except StopIteration:
    f.close()
View Code
#1 異常類只能用來處理指定的異常情況,如果非指定異常則無法處理。
s1 = hello
try:
    int(s1)
except IndexError as e: # 未捕獲到異常,程序直接報錯
    print e

#2 多分支
s1 = hello
try:
    int(s1)
except IndexError as e:
    print(e)
except KeyError as e:
    print(e)
except ValueError as e:
    print(e)

#3 萬能異常Exception
s1 = hello
try:
    int(s1)
except Exception as e:
    print(e)

#4 多分支異常與萬能異常
#4.1 如果你想要的效果是,無論出現什麽異常,我們統一丟棄,或者使用同一段代碼邏輯去處理他們,那麽騷年,大膽的去做吧,只有一個Exception就足夠了。
#4.2 如果你想要的效果是,對於不同的異常我們需要定制不同的處理邏輯,那就需要用到多分支了。

#5 也可以在多分支後來一個Exception
s1 = hello
try:
    int(s1)
except IndexError as e:
    print(e)
except KeyError as e:
    print(e)
except ValueError as e:
    print(e)
except Exception as e:
    print(e)

#6 異常的其他機構
s1 = hello
try:
    int(s1)
except IndexError as e:
    print(e)
except KeyError as e:
    print(e)
except ValueError as e:
    print(e)
#except Exception as e:
#    print(e)
else:
    print(try內代碼塊沒有異常則執行我)
finally:
    print(無論異常與否,都會執行該模塊,通常是進行清理工作)

#7 主動觸發異常
try:
    raise TypeError(類型錯誤)
except Exception as e:
    print(e)

#8 自定義異常
class EgonException(BaseException):
    def __init__(self,msg):
        self.msg=msg
    def __str__(self):
        return self.msg

try:
    raise EgonException(類型錯誤)
except EgonException as e:
    print(e)

#9 斷言:assert 條件
assert 1 == 1  
assert 1 == 2

#10 總結try..except

1:把錯誤處理和真正的工作分開來
2:代碼更易組織,更清晰,復雜的工作任務更容易實現;
3:毫無疑問,更安全了,不至於由於一些小的疏忽而使程序意外崩潰了;

python學習點滴記錄-Day08