PythonI/O進階學習筆記_3.2面向對象編程_python的封裝

 

前言:html

本篇相關內容分爲3篇多態、繼承、封裝,這篇爲第三篇 封裝。java

本篇內容圍繞 python基礎教程這段:
在面向對象編程中,術語對象大體意味着一系列數據(屬性)以及一套訪問和操做這些數據的方法。使用對象而非全局變量和函數的緣由有多個,下面列出了使用對象的最重要的好處。
 多態:可對不一樣類型的對象執行相同的操做,而這些操做就像「被施了魔法」同樣可以正常運行。
 封裝:對外部隱藏有關對象工做原理的細節。
 繼承:可基於通用類建立出專用類。
內容較多,這篇爲下篇。

Content:python

- 封裝c++

1.數據封裝和私有屬性算法

2. 類變量和實例變量(對象變量)sql

3. 類屬性和實例屬性得查找順序(MRO)編程

4. 靜態方法 類方法和對象方法使用以及參數安全

5. python的接口和自省機制app

6. 上下文管理器框架

====================================

1.python的數據封裝和私有屬性

a.python的__私有屬性

python用__開頭完成私有屬性的封裝。用__開頭的屬性名或者方法就無法直接外部獲取,只有類中的公共方法才能夠訪問。

python的數據封裝和java c++這種靜態語言不一樣的是,靜態語言其實自己有private類型的。而python是用小技巧實現了這種私有屬性。

 
b.如何實現的?
以__開頭的變量,python會對它進行變形,變形的模式是加上當前的class和變形的名稱。
例: 以下的User中的__birthday變量,最後調用的時候變爲 _User__birthday
實際上在python中這並非真正的安全。其實即便在java裏的private關鍵詞對反射機制比較瞭解其實也不是絕對安全性。
python比java突破這個安全性更加簡單。
 
c.通常什麼狀況用這種私有屬性?
  • 隱藏起一個屬性,不想讓外部調用
  • 保護這個屬性,不想讓這個屬性隨意改變
  • 保護這個屬性不被子類繼承

 

2.類變量和實例變量

a.什麼是python的類變量?

類下的變量。

例:
class A():
    aa=1
其中aa就是類變量。
 
b.什麼是實例變量?
class A:
    aa=1
    def __init__(self,x,y):
        self.x=x
        self.y=y
其中,self是傳遞的對象(實例自己),x,y是對象參數。
先查找對象變量,再查找類變量。
 
c.類變量和實例變量的區別
類A是沒有對象x和y的。 可是實例化的a有。
 
d.類變量被修改的影響 和實例變量被修改的影響
 
若是修改實例對象中aa的值,修改結果如何改變呢?
修改對象變量的類變量的值的時候,至關於多新建了一個對象a中的aa的值,而且實例a的對象值得查找順序是:
先查找本身實例中,是否有這個變量,再往上查找。
 
3. 類屬性和實例屬性得查找順序(MRO)
在繼承那篇中,就有講到MRO查找順序,可是重點是在於查找子類的父類繼承順序,那麼同理獲得的屬性查找順序是什麼樣的呢?
回顧一下C3算法,這篇講的比較明白: https://blog.csdn.net/u011467553/article/details/81437780
我們再來看下面的輸入,是否能看出輸出是什麼呢?
class Init(object):
    def __init__(self, v):
        #print("init")
        self.val = v
        #print("init:",self.val)
class Add2(Init):
    def __init__(self, val):
        #print("Add2")
        super(Add2, self).__init__(val)
        #print("add2:",self.val)
        self.val += 2
class Mult(Init):
    def __init__(self, val):
        #print("Mult")
        super(Mult, self).__init__(val)
        #print("Mult:",self.val)
        self.val *= 5
class HaHa(Init):
    def __init__(self, val):
        #print("HAHA")
        super(HaHa, self).__init__(val)
        #print("Haha:",self.val)
        self.val /= 5
class Pro(Add2,Mult,HaHa): #
    pass
class Incr(Pro):
    def __init__(self, val):
        super(Incr, self).__init__(val)
        self.val+= 1
# Incr Pro Add2 Mult HaHa Init
p = Incr(5)
print(p.val)
c = Add2(2)
print(c.val)

 把代碼中的print註釋都拿掉,能夠發現整個流程爲:

 

4. 靜態方法 類方法和對象方法使用以及參數

a.靜態方法 staticmethod

- 靜態方法定義:

   使用裝飾器@staticmethod。參數隨意,沒有「self」和「cls」參數,可是方法體中不能使用類或實例的任何屬性和方法

- 靜態方法的一些特色:

   靜態方法是類中的函數,不須要實例。

   靜態方法主要是用來存放邏輯性的代碼,主要是一些邏輯屬於類,可是和類自己沒有交互,即在靜態方法中,不會涉及到類中的方法和屬性的操做。

   能夠理解爲將靜態方法存在此類的名稱空間中。事實上,在python引入靜態方法以前,一般是在全局名稱空間中建立函數。- 

- 靜態方法調用:

例:我要在類中實現一個獲得如今時間的方法。與傳進去的任何參數都無關那種。

import time


class TimeTest(object):
    def __init__(self, hour, minute, second):
        self.hour = hour
        self.minute = minute
        self.second = second

    @staticmethod
    def showTime():
        return time.strftime("%H:%M:%S", time.localtime())


print(TimeTest.showTime())
t = TimeTest(2, 10, 10)
nowTime = t.showTime()
print(nowTime)

 

b.類方法

- 類方法定義:

    使用裝飾器@classmethod。第一個參數必須是當前類對象,該參數名通常約定爲「cls」,經過它來傳遞類的屬性和方法(不能傳實例的屬性和方法)

- 類方法的一些特色:

    將類自己做爲對象進行操做。

    無論這個方式是從實例調用仍是從類調用,它都用第一個參數把類傳遞過來

- 類方法使用:

例:實現一個基類作一個有顏色屬性的抽象共性,對於實際的顏色的值須要結合實際子類傳遞的值進行匹配

class ColorTest(object):
    color = "color"

    @classmethod
    def value(self):
        return self.color
class Red(ColorTest):
    color = "red"
class Green(ColorTest):
    color = "green"
g = Green()
print(g.value())
print(Green.value())

這時候可能有人會以爲,這個跟實例方法(普通方法)不是同樣的嘛,繼承父類,而且用繼承中的知識重寫父類中的屬性或者方法?

重點就在於,若是咱們把@classmethod這個方法去掉,Green.value()這樣去調用是會報錯的。

 

由於須要傳遞實例參數進去,而不能直接用類調用。

 

c.對象方法(實例方法)

- 實例方法定義:

    第一個參數必須是實例對象,該參數名通常約定爲「self」,經過它來傳遞實例的屬性和方法(也能夠傳類的屬性和方法)

- 實例方法的一些特色:

    只能由類的實例來調用,就是咱們平時最經常使用的。

比較簡單,沒有特殊裝飾器,暫不舉例。

 

5.python的接口和自省機制

a.什麼是python的自省機制

當咱們須要實現一個通用的DBM框架時,可能須要對數據對象的字段賦值,但咱們沒法預知用到這個框架的數據對象都有些什麼字段,換言之,咱們在寫框架的時候須要經過某種機制訪問未知的屬性。

也就是說,咱們須要在不少時候去訪問python本身爲咱們作了哪些隱藏的事情,或者某個框架裏具體實現了哪些方法等。

經過python的自省機制去讓python告訴咱們,咱們查詢的對象是什麼,有哪些功能等。

 

b.python自省之訪問對象的屬性

例有下面這個類,而且實例化了一個a對象。

class Company(object):
    def __init__(self,company_name,staffs=[]):
        self.company_name=company_name
        self.staffs=staffs
    def add(self,staff):
        self.staffs.append(staff)
    def remove(self,staff):
        self.staffs.remove(staff)

user_list=['tangrong1','tangrong2','tangrong3']
a=Company("aaa",user_list)

我須要去獲得類裏的一些方法和屬性:

####訪問對象的屬性
#dir() 調用這個方法將返回包含obj大多數屬性名的列表(會有一些特殊的屬性不包含在內)。obj的默認值是當前的模塊對象。
print("dir()")
print(dir(Company))
print(dir(a))
#hasattr(obj,attr) 這個方法用於檢查obj是否有一個名爲attr的值的屬性,返回一個布爾值
print("hasattr()")
print(hasattr(a,"add"))
print(hasattr(a,"staffs"))
print(hasattr(a,"a"))
#getattr(obj, attr) 調用這個方法將返回obj中名爲attr值的屬性的值,例如若是attr爲'staffs',則返回obj.staffs。
print("getattr()")
print(getattr(a,"add"))
print(getattr(a,"staffs"))
#setattr(obj, attr, val) 調用這個方法將給obj的名爲attr的值的屬性賦值爲val。例如若是attr爲'bar',則至關於obj.bar = val。
print("setattr()")
print(setattr(a,"staffs",["tangrong4","tangrong5"]))
print(a.staffs)

輸出爲:

dir() ##ps:能夠發現,實例和類的dir()列出來的有些不同
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', 
'__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__',
'add', 'remove'] ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__',
'__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__',
'add', 'company_name', 'remove', 'staffs']
hasattr() True True False
getattr()
<bound method Company.add of <__main__.Company object at 0x000002D3172F2518>> ['tangrong1', 'tangrong2', 'tangrong3']
setattr() None [
'tangrong4', 'tangrong5']

 

c.python自省之訪問對象的元數據

包括各類__dict__()\__doc__\__bases__等

內容比較多,可看這篇:https://www.cnblogs.com/huxi/archive/2011/01/02/1924317.html

 

6.上下文管理器

a.什麼是上下文管理器?

上下文管理器就是實現了上下文管理協議的對象。主要用於保存和恢復各類全局狀態,關閉文件等,上下文管理器自己就是一種裝飾器。

感受上句很像白說是否是- -。實際例就是,相似於with語句,就是遵循了上下文管理協議,才能在內部咱們看不到的地方,幫咱們完成了退出時作出關閉文件、執行自定義代碼塊的操做的。就不用咱們顯示判斷調用讀取完了就關閉文件這種操做。

with open("test/test.txt","w") as f_obj:
  f_obj.write("hello")

b.用with看他遵循的上下文管理協議

上下文管理協議包括兩個方法:

  • contextmanager.__enter__() 從該方法進入運行時上下文,並返回當前對象或者與運行時上下文相關的其餘對象。若是with語句有as關鍵詞存在,返回值會綁定在as後的變量上。

  • contextmanager.__exit__(exc_type, exc_val, exc_tb) 退出運行時上下文,並返回一個布爾值標示是否有須要處理的異常。若是在執行with語句體時發生異常,那退出時參數會包括異常類型、異常值、異常追蹤信息,不然,3個參數都是None。

能用with語句的對象,也是由於這個對象裏,遵循了這個協議。

with語句就是爲支持上下文管理器而存在的,使用上下文管理協議的方法包裹一個代碼塊(with語句體)的執行,併爲try...except...finally提供了一個方便使用的封裝。

咱們建立一個能支持with(上下文管理協議)的類,這個類實現了db最開始創建鏈接,退出時關閉鏈接的操做。

import sqlite3
 
class DataConn:
  def __init__(self,db_name):
    self.db_name = db_name
 
  def __enter__(self):
    self.conn = sqlite3.connect(self.db_name)
    return self.conn
 
  def __exit__(self,exc_type,exc_val,exc_tb):
    self.conn.close()
    if exc_val:
      raise
 
if __name__ == "__main__":
  db = "test/test.db"
  with DataConn(db) as conn:
    cursor = conn.cursor()

 

c.用contextlib自定義上下文管理器

from contextlib import contextmanager
 
@contextmanager
def file_open(path):
  try:
    f_obj = open(path,"w")
    yield f_obj
  except OSError:
    print("We had an error!")
  finally:
    print("Closing file")
    f_obj.close()
 
if __name__ == "__main__":
  with file_open("test/test.txt") as fobj:
    fobj.write("Testing context managers")

或者簡單版:

 

這個裝飾器裝飾的必定要是生成器。
__enter__中的代碼都是yield以前的邏輯代碼。
__exit__代碼實在yield以後實現的代碼邏輯。
相關文章
相關標籤/搜索