1. 程式人生 > 其它 >python 面向物件(4)

python 面向物件(4)

python 面向物件(4)

反射的實際案例:

# 利用面向物件編寫系統的終端功能
class WinCmd(object): 
    def ls(self):
        print('windows系統正在執行ls命令')
    def dir(self):
        print('windows系統正在執行dir命令')
    def cd(self):
        print('windows系統正在執行cd命令')

class LinuxCmd(object):
    def ls(self):
        print('Linux系統正在執行ls命令')
    def dir(self):
        print('Linux系統正在執行dir命令')
    def cd(self):
        print('Linux系統正在執行cd命令')

obj = WinCmd()
obj1 = LinuxCmd()
"""反射提供了一種不需要考慮程式碼的前提下 操作資料和功能"""
def run(obj):
    while True:
        cmd = input('請輸入您的指令>>>:')
        if hasattr(obj, cmd):
            func_name = getattr(obj, cmd)
            func_name()
        else:
            print('cmd command not found')   
run(obj1)
run(obj)

面向物件雙下方法:

雙下str:

#__str__
物件被執行列印(print、前端展示)操作的時候自動觸發
     該方法必須返回字串型別的資料
  很多時候用來更加精準的描述物件

#程式碼示例:
class Course(object):

    def __init__(self, name, price):
        self.name = name
        self.price = price

    def __str__(self):
        return "func is __str__ ,課程:{},價格:{}".format(self.name, self.price)

#
python = Course("English", 250)

print(python)  # func is __str__ ,課程:English,價格:250

print("%s" % (python))  # func is __str__ ,課程:English,價格:250

print([python])  
#[<__main__.Course object at 0x0000023A660794E0>]

雙下str和雙下repr區別

# 如果str存在,repr也存在
     那麼print(obj)和使用字串格式化format,%s這兩種方式 呼叫的都是__str__
     而repr(obj)和%r格式化字串,都會呼叫__repr__
# 如果str不存在,repr存在
     那麼print(obj),字串格式化format,%s,%r 和repr(obj)都呼叫__repr__
# 如果str存在,repr不存在
     那麼print(obj)和使用字串格式化format,%s這兩種方式 呼叫的都是__str__
     repr(obj)和%r格式化字串 都會打印出記憶體地址

雙下del:

#__del__
	物件被執行(被動、主動)刪除操作之後自動執行
    
# 程式碼示例:
class A(object):
    def __init__(self, name,age):
        self.name = name
        self.age = age


    def __del__(self):
        print("hhhh")


f = A("jason", 18)
print(f.__dict__)  # {'name': 'Tom', 'age': 45}

del f.name   
'''只和del obj語法有關係 在執行del obj之前會來執行一下__del__中的內容'''
print(f.__dict__)
#{'age': 18} hhhh

雙下getattr, setattr, delattr, getattribute:

# __getattr__
 	物件查詢不存在名字的時候自動觸發
#__setattr__
	物件在執行新增屬性操作的時候自動觸發	>>>	obj.變數名=變數值
#__getattribute__
	只要物件查詢名字無論名字是否存在都會執行該方法
  如果類中有__getattribute__方法 那麼就不會去執行__getattr__方法

#程式碼示例:
class A(object):

    def __setattr__(self, key, value):
        print("執行了__setattr__方法")
        print(key, value)

    def __getattr__(self, item):
        print("執行了__getattr__方法")

    def __delattr__(self, item):
        print("執行了__delattr__方法")

    def __getattribute__(self, item):
        print("執行了__getattribute__方法")


f = A()
f.name = 'jason'  # 將會執行__setattr__

f.name  # 將會執行__getattribute__或者__getattr__ 當兩者都存在的時候 先執行__getattribute__ 如果兩者都沒有 報錯

del f.name  # 將會執行__delattr__

雙下call:

#__call__
	物件被加括號呼叫的時候自動觸發

#程式碼示例:
class A(object):
    def __init__(self, name,):
        self.name = name

    def __call__(self, *args, **kwargs):
        print("name " + self.name)


f = A("Tom")
f()  # name Tom

A("jason",)()  # name Mack   
''' 物件+()就會呼叫__call__()方法'''

雙下enter,exit:

#__enter__
	物件被執行with上下文管理語法開始自動觸發 
  	該方法返回什麼as後面的變數名就會得到什麼
    
#__exit__
	物件被執行with上下文管理語法結束之後自動觸發
    
# 程式碼示例:
class A(object):
    def __enter__(self):
        print("開始的時候觸發")
        return 'zz'	
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('結束的時候觸發')

with A() as f:
    print('hhaha')

print(f) #f 是__enter__返回的值 沒有返回值預設是None

#輸出結果:
開始的時候觸發
hhaha
結束的時候觸發
zz

雙下方法練習:

1.讓字典具備句點符查詢值得功能

class MyDict(dict):
      def __getattr__(self, item):
          return self.get(item)

      def __setattr__(self, key, value):
          self[key] = value
  '''要區別是名稱空間的名字還是資料k:v鍵值對'''

  obj = MyDict({'name':'jason','age':18})
  # 1.具備句點符取v
   print(obj.name)
   print(obj.age)
  # 2.具備句點符設k:v
  obj['gender'] = 'male'
  obj.pwd = 123  # 給字典名稱空間新增名字  不是資料k:v
  print(obj)

2.執行不報錯

#補全程式碼使其不報錯:
	class Context:
			pass
	with Context() as ctx:
			ctx.do_something()
    
#程式碼示例:

class Context:
    def __enter__(self):
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        pass
    def do_something(self):
        pass
    
with Context() as ctx:
    ctx.do_something()

元類

# 元類的介紹
Python當中萬物皆物件,我們用class關鍵字定義的類本身也是一個物件,負責產生該物件的類稱之為元類,元類可以簡稱為類的類

元類的主要目的是為了控制類的建立行為

class MyClass(object):
    pass
obj = MyClass()
print(type(obj))
print(type(MyClass))  # <class 'type'>

class Student:
    pass
print(type(Student))  # <class 'type'>

class Teacher(MyClass):
    pass
print(type(Teacher))  # <class 'type'>

'''type是Python的一個內建元類,用來直接控制生成類,在python當中任何class定義的類其實都是type類例項化的結果。

只有繼承了type類才能稱之為一個元類,否則就是一個普通的自定義類,自定義元類可以控制類的產生過程,類的產生過程其實就是元類

的呼叫過程'''

產生類的兩種形式

一個類由三大組成部分,分別是

1、類名class_name

2、繼承關係class_bases

3、類的名稱空間class_dict

#方式1:使用class關鍵字(python直譯器轉化為第二種)
class A(object):
    pass
	print(A)  # <class '__main__.A'>
    
#方式2:通過type關鍵字,依次傳入以上三個引數即可
type(類名,父類,類的名稱空間)
  	res = type('A', (), {})
		print(res)  # <class '__main__.A'>
'''
學習元類的目的
	元類能夠控制類的建立 也就意味著我們可以高度定製類的行為
		eg:掌握了物品的生產過程 就可以在過程中做任何的額外操作'''

元組基本使用

#要求類的名字必須首字母大寫

"""元類是不能通過繼承的方式直接指定的"""
	需要通過關鍵字引數的形式修改
  	class C1(metaclass=MyTypeClass):
    	pass
    
class MyTypeClass(type):
    def __init__(cls, cls_name, cls_bases, cls_dict):
        # print(cls, cls_name, cls_bases, cls_dict)
        if not cls_name.istitle():
            raise Exception("類名的首字母必須大寫 你個SD")
        super().__init__(cls_name, cls_bases, cls_dict)

class C1(metaclass=MyTypeClass):
    school = '清華大學'

元組的進階操作

1.回想__call__方法
	物件加括號會自動執行產生該物件的類裡面的__call__,並且該方法返回什麼物件加括號就會得到什麼
  推導:類加括號會執行元類的裡面的__call__該方法返回什麼其實類加括號就會得到什麼
  """類裡面的__init__方法和元類裡面的__call__方法執行的先後順序"""
  class MyTypeClass(type):
    def __call__(self, *args, **kwargs):
        print('__call__ run')
        super().__call__(*args, **kwargs)
  class MyClass(metaclass=MyTypeClass):
      def __init__(self, name):
          print('__init__ run')
          self.name = name 
  obj = MyClass('jason')
  
# 定製物件的產生過程
	class MyTypeClass(type):
    def __call__(self, *args, **kwargs):
        # print('__call__ run')
        # print(args,kwargs)
        if args:
            raise Exception('必須全部採用關鍵字引數')
        super().__call__(*args, **kwargs)

  class MyClass(metaclass=MyTypeClass):
      def __init__(self, name):
          # print('__init__ run')
          self.name = name

	"""強制規定:類在例項化產生物件的時候 物件的獨有資料必須採用關鍵字引數"""
  # obj1 = MyClass('jason')
  obj2 = MyClass(name='jason')
"""
如果你想高度定製類的產生過程
	那麼編寫元類裡面的__init__方法
如果你想高度定製物件的產生過程
	那麼編寫元類裡面的__call__方法
"""

雙下new方法

__new__用於產生空物件(類)		骨架
__init__用於例項化物件(類)		血肉

"""
注意:並不是所有的地方都可以直接呼叫__new__ 該方法過於底層
	如果是在元類的__new__裡面 可以直接呼叫
		class Meta(type):
      def __new__(cls, *args, **kwargs):
          obj = type.__new__(cls,*args,**kwargs)
          return obj
	如果是在元類的__call__裡面 需要間接呼叫
		class Mate(type):
    	def __call__(self, *args, **kwargs):
         obj = object.__new__(self) # 建立一個空物件
         self.__init__(obj,*args,**kwargs) # 讓物件去初始化
         return obj
"""