Python面向對象進階

上篇回顧python

  • 面向對象是一種編程方式,此編程方式的實現是基於對  和 對象 的使用
  • 類 是一個模板,模板中包裝了多個「函數」供使用(能夠講多函數中公用的變量封裝到對象中)
  • 對象,根據模板建立的實例(即:對象),實例用於調用被包裝在類中的函數
  • 面向對象三大特性:封裝、繼承和多態
  • 靜態屬性(@property) 特色:將函數屬性假裝成數據屬性(封裝邏輯)
  • 靜態方法(@staticmethod) 跟類和實例無關係,名義上歸屬類管理,但不能使用類變量和實例變量
  • 類方法(@classmethod) 跟實例沒有關係,類本身調用;只能訪問類的屬性,不能訪問實例屬性,不須要self參數,自動加cls參數
  • 面向對象的專業術語

本篇介紹程序員

  反射編程

  • 根據字符串的形式去某個對象中操做它的成員
  • 四個能夠實現反射的函數(也適用於對象和類)
class Sea: #
    def __init__(self,name,country,addr):
        self.name = name
        self.country = country
        self.addr = addr
    def sea_wave(self):
        print("一波兒海嘯正在來襲")
s1 = Sea("東海","哥雅王國","風車村")

print(hasattr(s1,"name")) # 判斷有沒有
print(getattr(s1,"name123")) # 找不到報錯
print(getattr(s1,"name123",None)) # 能夠設置返回值則不報錯
del s1.name           # 方式1
print(s1.__dict__)
delattr(s1,"name")     # 方式2
print(s1.__dict__)
setattr(s1,"age",10000) # 設置一個東西
print(s1.__dict__)
反射函數的用法

  爲何用到反射(舉個簡單的小例子)服務器

    有倆程序員,一個alex,一個是egon,alex在寫程序的時候須要用到egon所寫的類,可是egon去跟女友度蜜月去了,尚未完成他寫的類,alex想到了反射,使用了反射機制alex能夠繼續完成本身的代碼,等egon度蜜月回來後再繼續完成類的定義而且去實現alex想要的功能。網絡

    總之反射的好處就是,能夠事先定義好接口,接口只有在被完成後纔會真正執行,這實現了即插即用,這實際上是一種‘後期綁定’,什麼意思?即你能夠事先把主要的邏輯寫好(只定義接口),而後後期再去實現接口的功能app

?
1
2
3
4
5
class FtpClient:
     'ftp客戶端,可是還麼有實現具體的功能'
     def __init__( self ,addr):
         print ( '正在鏈接服務器[%s]' % addr)
         self .addr = addr
#from module import FtpClient
f1=FtpClient('192.168.1.1')
if hasattr(f1,'get'):
    func_get=getattr(f1,'get')
    func_get()
else:
    print('---->不存在此方法')
    print('處理其餘的邏輯')

不影響alex的代碼編寫

  三個參數,給對象添加屬性ide

    這是python解釋器底層的內置方法。固然咱們也能夠對它們進行一些操做函數

    __setattr__   添加/修改屬性會觸發它的執行post

    __delattr__  刪除屬性的時候會觸發spa

    __getattr__   只有在使用點調用屬性且屬性不存在的時候纔會觸發

    做用:系統內置函數屬性(你定義了就用你定義的函數屬性,不定義就用系統默認的函數屬性)

class Foo:
    x=1
    def __init__(self,y):
        self.y=y

    def __getattr__(self, item):
        print('----> from getattr:你找的屬性不存在')


    def __setattr__(self, key, value):
        print('----> from setattr')
        # self.key=value #這就無限遞歸了,你好好想一想
        # self.__dict__[key]=value #應該使用它

    def __delattr__(self, item):
        print('----> from delattr')
        # del self.item #無限遞歸了
        self.__dict__.pop(item)

#__setattr__添加/修改屬性會觸發它的執行
f1=Foo(10)
print(f1.__dict__) # 由於你重寫了__setattr__,凡是賦值操做都會觸發它的運行,你啥都沒寫,就是根本沒賦值,除非你直接操做屬性字典,不然永遠沒法賦值
f1.z=3
print(f1.__dict__)

#__delattr__刪除屬性的時候會觸發
f1.__dict__['a']=3#咱們能夠直接修改屬性字典,來完成添加/修改屬性的操做
del f1.a
print(f1.__dict__)

#__getattr__只有在使用點調用屬性且屬性不存在的時候纔會觸發
f1.xxxxxx
綜合示例
class Foo:
    x=1
    def __init__(self,y):
        self.y=y

    def __getattr__(self, item):                   # __getattr__經常使用,且須要記憶
        print('執行__getattr__')

f1=Foo(10)
print(f1.y)

#沒有的時候就會觸發: __getattr__
print(getattr(f1,'y'))  #len(str)---->str.__len__()
f1.ssssssssssssssssssssssssssssss

10
10
執行__getattr__

  動態導入模塊

  一、新建一個t.py的文件

print('---------------->')
def test1():
    print('test1')

def _test2():
    print('test2')

  二、再建立:m1文件夾,再在他下面建立一個t.py

module_t=__import__('m1.t')
print(module_t)
module_t.t.test1()
# from m1.t import *
# from m1.t import test,_test2

import importlib
m=importlib.import_module('m1.t')
print(m)
m.test1()
m._test2()
---------------->
<module 'm1' (namespace)>
test1
<module 'm1.t' from 'D:\\python\\day26\\m1\\t.py'>
test1
test2
輸出

  二次加工標準類型(包裝)

    包裝:python爲你們提供了標準數據類型,以及豐富的內置方法,其實在不少場景下咱們都須要基於標準數據類型來定製咱們本身的數據類型,新增/改寫方法,這就用到了咱們剛學的繼承/派生知識(其餘的標準類型都可以經過下面的方式進行二次加工)

 1 # 二次加工標準類型
 2 class List(list):
 3     def append(self,p_object):
 4         if type(p_object) is str:
 5             super().append(p_object)
 6         else:
 7             print("添加的類型必須爲字符串類型")
 8 
 9 l = List(["hello","world"])
10 l.append(1)
11 print(l)
12 
13 class Me(list):
14     def remove(self,value):
15         print("什麼都不能刪除")
16 
17 m = Me([1,2,3,4])
18 m.remove(2)
例子,對追加和刪除的二次加工

  受權

    受權是包裝的一個特性, 包裝一個類型一般是對已存在的類型的一些定製,這種作法能夠新建,修改或刪除原有產品的功能。其它的則保持原樣。受權的過程,便是全部更新的功能都是由新類的某部分來處理,但已存在的功能就受權給對象的默認屬性。

    實現受權的關鍵點就是覆蓋__getattr__方法

class Filehandle:
    def __init__(self,filename,mode="r",encoding="utf-8"):
        self.file = open(filename,mode,encoding=encoding)
        self.mode = mode
        self.encoding = encoding

    def __getattr__(self, item):
        print(item,type(item)) # read <class 'str'>
        self.file.read()  # self.file 裏面有read方法
        return self.file,item

f1=Filehandle('a.txt','r')
# print(f1.file)     #  <_io.TextIOWrapper name='a.txt' mode='r' encoding='utf-8'>
print(f1.__dict__)   # 對象的屬性字典中沒有read方法,觸發__getattr__方法
#{'file': <_io.TextIOWrapper name='a.txt' mode='r' encoding='utf-8'>, 'encoding': 'utf-8', 'mode': 'r'}
print(f1.read)       # 從而找到read方法

sys_F = Filehandle("b.txt","w+") # 從Filehandle中建立實例
print('---->',getattr(sys_F,'read')) 
# ----> (<_io.TextIOWrapper name='b.txt' mode='w+' encoding='utf-8'>, 'read')
文件下的受權
import time
class Filehandle:
    def __init__(self,filename,mode="r",encoding="utf-8"):
        self.file = open(filename,mode,encoding=encoding)
        self.mode = mode
        self.encoding = encoding
    def write(self,line):
        print("--------> ",line) # 輸出打印的內容
        t = time.time()
        t = time.strftime('%Y-%m-%d %X') 
        self.file.write("%s %s" %(t,line)) # self.file 也有寫的方法。

    def __getattr__(self, item):
    #     print(item,type(item)) # read <class 'str'>
    #     self.file.read()  # self.file 裏面有read方法
        return self.file,item

f1=Filehandle('a.txt','r')
# print(f1.file)     #  <_io.TextIOWrapper name='a.txt' mode='r' encoding='utf-8'>
print(f1.__dict__)   # 對象的屬性字典中沒有read方法,觸發__getattr__方法
#{'file': <_io.TextIOWrapper name='a.txt' mode='r' encoding='utf-8'>, 'encoding': 'utf-8', 'mode': 'r'}
print(f1.read)       # 從而找到read方法

sys_F = Filehandle("b.txt","w+") # 從Filehandle中建立實例
# print('---->',getattr(sys_F,'read'))
# ----> (<_io.TextIOWrapper name='b.txt' mode='w+' encoding='utf-8'>, 'read')

sys_F.write("路飛\n")
sys_F.write("娜美\n")
文件讀寫的受權

   類的特殊成員

class Foo:
    def __init__(self):
        print("我是init")
    def __call__(self, *args, **kwargs):
        print("我是call")
        return 1
# r = Foo()  類的實例化,執行init方法
# r() 對象加括號什麼鬼?   實際上是調用了__call__
print(Foo()()) 

我是init
我是call
1
__call__

 

class Foo:
    def __init__(self):
        pass
    def __call__(self, *args, **kwargs):
        pass
    def __getitem__(self, item):
        print(item)
    def __setitem__(self, key, value):
        print(key,value)
    def __delitem__(self, key):
        print(key)
r = Foo()  #類的實例化,執行init方法
r() #對象加括號什麼鬼?   實際上是調用了__call__
# print(Foo()())

r["getitem"] # __getitem__   
r["gs"] = 123 # __setitem__
del r["gs"] # __delitem__

getitem
gs 123
gs
__getitem__  __setitem__  __delitem__

   對三者的補充,再經過對列表索引切片的時候,查看,賦值,刪除也是對應執行它們。

   經過字典的操做的方式是調用它們,而經過點的方式去取值是調用attr這些

#例子2
class Bar:
    def __init__(self):
        pass
    def __getitem__(self, item):
        print("__getitem__",item)
    def __setitem__(self, key, value):
        print("__setitem__")
        self.__dict__[key] = value
    def __delitem__(self, key):
        print("__delitem__")
        self.__dict__.pop(key)
b = Bar()
b["name"] = "alex"
print(b.__dict__)
del b["name"]
print(b.__dict__)
print(b["name"])


__setitem__
{'name': 'alex'}
__delitem__
{}
__getitem__ name
None
__getitem__,__setitem__,__delitem__

 

獲取類或者對象裏面的全部字段

例子未補充
__dict__

 

class C:
    def __init__(self,n):
        self.n = n
    def __iter__(self):
        return self
    def __next__(self):
        if self.n == 13:
            raise StopIteration
        self.n += 1
        return self.n
c = C(10)

for i in c: # for循環的強大機制,捕捉到StopIteration會結束  c.__iter__()
    print(i) # next(i) ----> i.__next__()
__iter__

 

class Onepiece:
    def __init__(self,name):
        self.name = name

class Sea(Onepiece):
    def __init__(self):
        pass
s = Sea()
o = Onepiece("路飛")
print(isinstance(o,Onepiece)) # 判斷對象是否是該類下的
print(isinstance(s,Onepiece)) # 若是存在繼承關係,則也是父類的對象

print(issubclass(Sea,Onepiece)) # 判斷是否爲子類
print(issubclass(Onepiece,Sea))
isinstance,issubclass

 

class Foo:
    x = 1
    def __init__(self,y):
        self.y = y

    def __getattr__(self, item):
        print("執行的是getattr",item)

    def __getattribute__(self, item):
        print("--->執行的是__getattribute__",item)
        raise AttributeError # 拋出一個異常交給__getattr__
    # 上面都是本身定義咱們想要的的機制過程,若是不寫,系統也有一套系統默認的這種機制。

f = Foo(10)
f.xx

--->執行的是__getattribute__ xx           # 是大哥
執行的是getattr xx                                 # 是小弟
__getattr__,__getattribute__

   屬性有沒有都會觸發__getattribute__

 1 class Fun:
 2     def __init__(self,name,age):
 3         self.name = name
 4         self.age = age
 5 
 6     # def __str__(self):
 7     #     return "%s %s " %(self.name,self.age)
 8     def __repr__(self):
 9         return "--->%s %s " %(self.name,self.age)
10 
11 me = Fun("Tom",18)
12 print(me) # 輸出<__main__.Fun object at 0x000001AC455E7400>
13 # 若是加上__str__
14 print(me) # Tom 18
15 
16 # 若是__str__和__repr__都存在
17 print(me)  # str(me) >>> me.__str__() >>> me.__repr__()    順序
18 # 若是__str__沒有被定義,那麼就會使用__repr__來代替輸出
19 # 注意:這倆方法的返回值必須是字符串,不然拋出異常
__str__,__repr__

 

class Pinjie:
    def __init__(self,day,mouth,year):
        self.day = day
        self.mouth = mouth
        self.year = year

    def __format__(self, format_spec):
        return "{}-{}-{}" .format(self.mouth,self.day,self.year)

time = Pinjie(26,12,2017)
print(format(time))  # format()就是在執行__format__


# 也能夠經過字典的方式進行指定格式。在這裏寫的比較硬
__format__

 

class A: # 定義__slots__以後 就沒有了實例的屬性字典
    __slots__ = "x"

a = A()
print(A.__dict__)
a.x = 2
a.y = 3  #報錯


# __slots__ 稱之爲類字典,定義類變量以後,就沒有了實例的屬性字典,訪問會報錯
# 並且全部產生的實例都只有類變量下的東西,且不能再進行添加
# 好處:節省內存 弊端:失去擴展性
__slots__

 

輸出文檔註釋信息,並且註釋信息不能被繼承
__doc__

 

1 # __moudle__ 獲取當前導入的模塊名
2 
3 # __class__ 獲取類名 
__class__,__moudle__

 

class B:
    def __init__(self,name):
        self.name = name
    def __del__(self): # 析構方法,當結束運行,會執行析構方法。
        print("執行了!")

b = B("tom")
del b
print("---------->")


#析構方法,當對象在內存中被釋放時,自動觸發執行。
#注:此方法通常無須定義,由於Python是一門高級語言,程序員在使用時無需關心內存的分配和釋放,由於此工做都是交給Python解釋器來執行,因此,析構函數的調用是由解釋器在進行垃圾回收時自動觸發執行的。
__del__

 

#一、操做文件寫法
with open('a.txt') as f:
   '代碼塊'
#二、上述叫作上下文管理協議,即with語句,爲了讓一個對象兼容with語句,必須在這個對象的類中聲明__enter__和__exit__方法
class Open:
    def __init__(self,name):
        self.name=name

    def __enter__(self):
        print('出現with語句,對象的__enter__被觸發,有返回值則賦值給as聲明的變量')
        # return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with中代碼塊執行完畢時執行我啊')

with Open('a.txt') as f:
    print('=====>執行代碼塊')
    # print(f,f.name)
輸出
出現with語句,對象的__enter__被觸發,有返回值則賦值給as聲明的變量
=====>執行代碼塊
with中代碼塊執行完畢時執行我啊

#三、執行代碼塊
__exit__()中的三個參數分別表明異常類型,異常值和追溯信息,with語句中代碼塊出現異常,則with後的代碼都沒法執行

沒有異常的狀況下,整個代碼塊運行完畢後去觸發__exit__,它的三個參數都會執行
class Foo:
    def __init__(self,name):
        self.name=name

    def __enter__(self):
        print('執行enter')   
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('執行exit')
        print(exc_type)
        print(exc_val)
        print(exc_tb)
        

with Foo('a.txt') as f:
    print(f)             #<__main__.Foo object at 0x012E8A90>
    print(assfsfdsfd)  
    print(f.name)      
print('00000000')
輸出
執行enter
Traceback (most recent call last):
<__main__.Foo object at 0x01478A90>
  File "D:/python/day28/s1.py", line 56, in <module>
執行exit
    print(assfsfd)   #觸發__exit__
<class 'NameError'>
NameError: name 'assfsfd' is not defined
name 'assfsfd' is not defined
<traceback object at 0x01484710>

若是__exit()返回值爲True,那麼異常會被清空,就好像啥都沒發生同樣,with後的語句正常執行
#沒有return True就會報錯,若是有return True異常本身吃了,不報異常


#用途:
#1.使用with語句的目的就是把代碼塊放入with中執行,with結束後,自動完成清理工做,無須手動干預
#2.在須要管理一些資源好比文件,網絡鏈接和鎖的編程環境中,能夠在__exit__中定製自動釋放資源的機制,你無須再去關係這個問題,這將大有用處
__enter__和__exit__

 

      未完待續……

相關文章
相關標籤/搜索