VB.net 2010 視頻教程 VB.net 2010 視頻教程 VB.net 2010 視頻教程
SQL Server 2008 視頻教程 c#入門經典教程 Visual Basic從門到精通視頻教程
當前位置:
魔兽世界wow > 編程開發 > Python >
  • python基礎教程之day29 ( exec , 元類,__new__, __call__

  • 2019-05-27 16:00 來源:未知

1,類也是對象

復制代碼
 魔兽世界wow www.geyjm.icu 
'''
動態語言
    可以在運行期間 動態生成類 修改對象屬性
靜態語言
'''''

'''
type(object_or_name, bases, dict)
type(object) -> the object's type
type(name, bases, dict) -> a new type
'''
obj = type("TestClass",(object,),{})
print(obj)          #<class '__main__.TestClass'>

class B(object):    #type("B",(object,),{"name":"rose"})
    name = "rose"


#模擬解釋器創建類的過程
def test1(a):
    print(a)

def test2(self,b):
    print(self,b)

class_name = "C"
bases = (object,)
name_dic = {"name":"jack","test1":test1,"test2":test2}

C = type(class_name,bases,name_dic)
# print(C)    #<class '__main__.C'>
c1 = C()
print(c1)      #<__main__.C object at 0x000001EFEC877550>
c1.test2(100)   #<__main__.C object at 0x0000021F31807550> 100
復制代碼

 

2 , exec

復制代碼
glob = {}
locl = {}

code = """
def test(a):
    print(a)
"""

exec(code,glob,locl)

# print(glob)   #打印系統內置函數
print(locl)     #{'test': <function test at 0x000001376F36C2F0>}
locl["test"](100)

#exec 可以執行字符串形式的python代碼,并且會把執行過程中產生的名字,放到局部名稱空間中
class_test = """
class A:
    def test(self):
        print(self)
"""
loca2 = {}
exec(class_test,None,loca2)
print(loca2)        #{'A': <class '__main__.A'>}

# eval(class_test)  #報錯,eval用于處理單行字符串的表達式
復制代碼

 

3,元類

復制代碼
class person():

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

    def SAYHI(self):
        print(self.name)

'''
類名必須大寫開頭,方法名必須全部小寫
應用場景:用于限制類,滿足某些條件,例如上述要求
type類已經具備了創建類的能力,但是現在不能滿足我們的要求
需要對已有的功能進行擴展或修改
方式:
    1,直接修改源代碼 ,行不通
    2,自定義新的元類
'''
class MyMetaClass(type):
    pass


#默認創建類時,是找的type來實例化的
class Person(metaclass=MyMetaClass):
    pass

class Student:
    def __init__(self):
        pass

print(type(Person))     #<class '__main__.MyMetaClass'>
print(type(Student))    #<class 'type'>

s = Student()           #實例化對象時, 1,產生空對象,2,自動執行__init__


#創建類對象時也是一樣的,會先創建空的類對象,在調用__init__()方法
# Person = MyMetaClass()

復制代碼
復制代碼
class MyMetaClass(type):
    def __init__(self,class_name,bases,name_dic):
        #元類中的self表示的都是類對象
        print(self)     #<class '__main__.Student'>

        #不要忘記調用父類的初始化
        super().__init__(class_name,bases,name_dic)
        print(name_dic)

        #類名必須首字母大寫,否則直接拋出異常
        if not class_name.istitle():
            print('類名必須大寫!')
            raise Exception

        #控制類中方法名必須全部小寫
        for k in name_dic:
            if str(type(name_dic[k])) == "<class 'function'>":
                if not k.islower():
                    raise Exception('方法名必須全小寫!')

#會自動調用其元類中的__init__方法傳入,類對象本身,類名稱,父類們,名稱空間
class Student(object,metaclass=MyMetaClass):    #MyMetaClass("Student",(object,),{})
    NAME = 10
    def asdfA(self):
        print('SNY')
復制代碼

 

3 , 元類中 __new__

復制代碼
class MeMetaClass(type):

    def __init__(self,class_name,bases,name_dic):
         # super().__init__(class_name,bases,name_dic)
         print("init")

    #該方法會在實例化類對象時自動調用并且在__init__之前調用
    #其作用是用于創建新的類對象的
    #注意這里必須調用type類中的__new__否則將無法產生類對象,并且返回其結果
    def __new__(cls, *args, **kwargs):
        #cls 表示元類自己即MyMetaClass
        print("new")
        print(args,kwargs)      #('Person', (), {'__module__': '__main__', '__qualname__': 'Person'}) {}
        return type.__new__(cls,*args,**kwargs)   #如果覆蓋__new__一定要寫上這行代碼

class Person(metaclass=MeMetaClass):
    pass

print(Person)   #<class '__main__.Person'>

#就算__init__中什么都不寫,這個類對象其實已經創建完成了,該有的屬性都有了
#這是與普通類不同之處

print(Person.__name__)  #不會報錯


class Student:
    def __init__(self,name):
        pass

s = Student('張三')
# s.name      #報錯
復制代碼

 

復制代碼
#練習1:

#需求:要求每個類必須包含__doc__屬性,__doc__用于訪問一個對象的注釋信息
class A:
    '''
    this is A
    '''
    pass

print(A.__doc__)

#如果你要控制類的創建,那就自定義元類 覆蓋__init__
class DocMetaClass(type):
    def __init__(self,class_name,bases,name_dic):
        super().__init__(class_name,bases,name_dic)

        # if not("__doc__" in name_dic and name_dic["__doc__"]):
        #     raise Exception

        #如果doc為空,則拋異常
        if not self.__doc__:
            raise Exception

class Person(metaclass=DocMetaClass):
    pass

print(type(object))
復制代碼

 

4 , 元類中 __call__ 

復制代碼
class Person:
    #調用對象時,會執行對象所在類中的__call__方法
    def __call__(self, *args, **kwargs):
        print("call")
        print(args)
        print(kwargs)

p = Person()
p()


'''
練習:將類中的為字符串的屬性名轉為大寫
'''
class MyMeta(type):
    #獲得某個類的實例
    def __call__(self, *args, **kwargs):
        print("call")
        # return super().__call__(*args,**kwargs)
        print(args)     #('jack', 'women', 18)
        print(kwargs)   #{}

        new_args = []
        for i in args:
            if isinstance(i,str):
                new_args.append(i.upper())
            else:
                new_args.append(i)
        print(new_args)     #['JACK', 'WOMEN', 18]
        return super().__call__(*new_args,**kwargs)

#注意:__new__  __init__是創建類對象時還會執行
#__call__ 類對象要產生實例時執行

class Student(metaclass=MyMeta):
    def __init__(self,name,gender,age):
        self.name = name
        self.gender = gender
        self.age = age

s = Student('jack','women',18)
print(s.age)        #18
print(s.gender)     #WOMEN

class Person(metaclass=MyMeta):
    def __init__(self,name,gender):
        self.name = name
        self.gender = gender

p = Person('rose','name')
print(p.name)   #ROSE
復制代碼

 

5, 單例模式

復制代碼
'''
單例模式
    是一種設計模式,是單個實例的意思
    當你需要 讓你的類僅有一個實例時,那就可以使用單例模式
'''''
class Person:
    obj = None
    def __init__(self,name,age,gender):
        self.name = name
        self.age = age
        self.gender = gender

    def say(self):
        print('my name is %s my 姑姑 is 龍媽'%self.name)

    @classmethod
    def get_instance(cls):
        if not cls.obj:
            obj = cls('小龍女',19,'women')
            cls.obj = obj
            print('創建新的了')
        return cls.obj
    

# 調用了多次,產生了多個實例  (地址不一樣)
p1 = Person('姬如雪','20','women')
p1.say()
p2 = Person('姬如雪','20','women')
p2.say()
print(p1,p2)  #<__main__.Person object at 0x000001A5A87076A0> <__main__.Person object at 0x000001A5A8707710>



#限制了類的實例化,如果為同一個類則只實例化一次 (地址一樣)
p1 = Person.get_instance()
p1.say()
p2 = Person.get_instance()
p2.say()

print(p1)   #<__main__.Person object at 0x000002396C2F75C0>
print(p2)   #<__main__.Person object at 0x000002396C2F75C0>
復制代碼

 

6 , 元類實現單例

復制代碼
class SingMeta(type):
    # 創建類時會執行__init__,在這為每一類設置一個obj屬性 默認為None
    def __init__(self, a, b, c):
        super().__init__(a, b, c)
        self.obj = None
        print(self.obj)

    # 當類要創建對象時會執行該方法
    def __call__(self, *args, **kwargs):
        # 判斷這個類如果已經有了實例了就直接返回,從而實現單例
        if self.obj:
            return self.obj

        # 如果沒有,則創建新的實例保存到類中
        obj = type.__call__(self, *args, **kwargs)
        self.obj = obj
        return obj


class Person(metaclass=SingMeta):
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender

    def say(self):
        print("my name is %s  my 姑姑 is 龍媽" % self.name)


class Student(metaclass=SingMeta):
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender=gender

    def say(self):
        print("my name is %s  my 姑姑 is 龍媽" % self.name)

p1 = Person('姬如雪',20,'women')
p2 = Person('姬如雪',20,'women')
print(p1)       #<__main__.Person object at 0x000001F512867668>
print(p2)       #<__main__.Person object at 0x000001F512867668>

stu1 = Student('史蒂夫',18,'man')
stu2 = Student('史蒂夫',18,'man')
print(stu1)     #<__main__.Student object at 0x0000013B18EB7780>
print(stu2)     #<__main__.Student object at 0x0000013B18EB7780>
復制代碼

 

7, 單例案例:

復制代碼
class Single(type):
    def __init__(self,a,b,c):
        super().__init__(a,b,c)
        self.obj = None

    def __call__(self, *args, **kwargs):
        if self.obj:
            return self.obj

        obj = type.__call__(self,*args,**kwargs)
        self.obj = obj
        return obj

class QQPlayer(metaclass=Single):
    def __init__(self,voice_value,repeat=False):
        self.voice_value =voice_value
        self.repeat = repeat

    def play(self,file_path):
        if hasattr(self,'file_path'):
            self.stop()

        print('正在播放%s'%file_path)
        self.file_path = file_path

    def stop(self):
        print('%s--停止播放'%self.file_path)

#問題:每次播放一次,都是實例化一次
ply = QQPlayer(100,True)
ply.play('如果.mp3')

ply1 = QQPlayer(100,True)
ply1.play('后來.mp3')

ply2 = QQPlayer(100,True)
ply2.play('田鷗.mp3')


print(ply)      #<__main__.QQPlayer object at 0x000002B349C87550>
print(ply1)     #<__main__.QQPlayer object at 0x000002B349C87550>
print(ply2)     #<__main__.QQPlayer object at 0x000002B349C87550>
復制代碼

 

8, 常見異常

復制代碼
'''
語法錯誤: SyntaxError: invalid syntax  
'''''
# if num > 1

# print('hello')
# a =
# b = 10

'''
類型錯誤: TypeError: 'int' object is not subscriptable
'''
# 1[:]

'''
下標錯誤: IndexError: list index out of range
'''
# li = [1,2,3,4]
# print(li[10])

'''
KeyError: 'xxx'
'''
# {'name':1}['xxx']

'''
FileNotFoundError: [Errno 2] No such file or directory: 'xxx'
'''
# with open('xxx')as f:
#     pass

'''
NameError: name 'name' is not defined
'''
# def func():
#     name
#     pass
# func()

'''
1,捕捉異常
'''
#語法一:
try:
    print('start')
    {'name':1}['xxx']
    print('over')
except:
    print(' key 不存在')


#語法二: 在except 中指定要處理的異常類型
try:
    print('start')
    {'name':1}['xxx']
    1[:]
    print('over')
except KeyError:
    print('出問題了')


#語法三 : 在except中可以指定多種異常類型,使用統一的處理方案,沒啥用
try:
    print('start')
    {'name':1}['xxx']
    1[:]
    print('over')
except(KeyError,TypeError):
    print('出問題了')


#語法四 : 一個try可以跟多個except語句 每個處理不同的異常
try:
    print('start')
    {'name': 1}['xxx