元类:

python中一切皆对象,意味着:

  1. 都可以被引用,如 x = obj

  2. 都可以被当做函数的参数传入

  3. 都可以被当做函数的返回值

  4. 都可以当做容器类的元素(列表、字典、元祖、集合),如: l = [func,time,obj,1]

换句话说,只要能满足上述4点,它就是对象;例如,类也是对象(类也是由type实例化产生的)

class Foo:
pass class Bar:
pass print(type(Foo))
print(type(Bar))
# 运行结果:
# <class 'type'>
# <class 'type'>

定义: 产生类的类称之为元类,默认所有用class定义的类,他们的元类都是type

定义类的两种方式:

  方式一:class方法(class方式也是利用了type这个元类)

  方式二:直接利用type这个元类的方法

定义类的三要素: 类名、类的基类、类的名称空间

"""利用class方法"""
class Chinese:
country = "China" def __init__(self,name,age):
self.name = name
self.age = age def talk(self):
print("%s is talking" %self.name) """利用type自己定义一个类"""
class_name = "Chinese" # 类名
class_base = (object,) # 它的基类;元祖的形式 # 类体(即 类内部的代码)
class_body = """
country = "China" def __init__(self,name,age):
self.name = name
self.age = age def talk(self):
print("%s is talking" %self.name)
"""
class_dict = {} # 类的名称空间 exec(class_body,globals(),class_dict) # 执行类体的代码,并把类体的值放入到 class_dict中作为其名称空间
print(class_dict)
# 打印结果:
# {'country': 'China', '__init__': <function __init__ at 0x000000D574701E18>, 'talk': <function talk at 0x000000D57487BD90>} # 定义类
Chinese1 = type(class_name,class_base,class_dict) # 这样就定义出了一个类 print(Chinese)
print(Chinese1)
# 打印结果:
# <class '__main__.Chinese'>
# <class '__main__.Chinese'> obj = Chinese("neo",18)
obj1 = Chinese1("egon",22) print(obj,obj.name,obj.age)
print(obj1,obj1.name,obj1.age)
# 打印结果:
# <__main__.Chinese object at 0x000000F1D54B67B8> neo 18
# <__main__.Chinese object at 0x000000F1D54B6710> egon 22

自定义元类控制类的行为:

利用class定义类(如上面的 Chinese)时,完整的写法其实是: class Chinese(object,metaclass = type)  # metaclass = type的含义是 元类是type ; “class Chinese:” 这行代码在执行(即定义Chinese类)时,其实是 Chinese = type(...) ,也就是 type这个元类在实例化。

如下代码:

"""不以type作为元类,而以自己定义的类作为元类,来控制类的行为"""

"""第二步的分析:创建 Mymeta 元类"""
class Mymeta(type): # class 是调用type这个元类; type元类里面已经定义了很多的方法,而我只是重新定义其中一小部分代码,所以我创建的元类需要继承type,从而Mymeta能够调用type的其他方法
"""
Chinese 的元类是这个Mymeta,在定义Chinese类的时候需要实例化Mymeta,
如: Chinese = Mymeta(class_name,class_bases,class_dict)
需要往Mymeta中传入定义类的三要素,所以 Mymeta中需要有 __init__ 方法
"""
def __init__(self,class_name,class_bases,class_dict): # 这个实例化的__init__ 肯定有这三个参数:class_name,class_bases,class_dict
super(Mymeta,self).__init__(class_name,class_bases,class_dict) # type 的 __init__ 方法中有很多的功能,自己创建的元类 Mymeta 首先需要继承type的__init__ , 同时再定义自己的方法 print(class_name)
print(class_bases)
print(class_dict) """第一步的分析:不以type作为元类,而以我自己创建的类 Mymeta 作为元类;所以我需要先定义一个元类 Mymeta"""
class Chinese(object,metaclass=Mymeta):
country = "China" def __init__(self,name,age):
self.name = name
self.age = age def talk(self):
print("%s is talking" %self.name) # 类体代码会以字典的形式传入到class_dict中作为 Chinese类的名称空间 # 运行结果:
# Chinese
# (<class 'object'>,)
# {'__module__': '__main__', '__qualname__': 'Chinese', 'country': 'China', '__init__': <function Chinese.__init__ at 0x00000075D7EDBD90>, 'talk': <function Chinese.talk at 0x00000075D7EDBE18>} """执行上述代码,直接就执行了 Mymeta 中 __init__ 的三个print,是因为 class Chinese(object,metaclass=Mymeta) 就是Mymeta在进行实例化,而实例化时会自动调用 __init__"""

补充知识点: raise TypeError("类型错误")  # raise 是主动抛出异常,程序就不往下走了

自定义元类控制类的行为示例2:(类名首字母大写)

class Mymeta(type):
def __init__(self,class_name,class_bases,class_dict):
"""目标功能:如果类名的首字母没有大写,直接报错"""
if not class_name.istitle(): # .istitle() 用于判断字符串首字母是否大写,返回bool值
raise TypeError("类名首字母必须大写") # 首字母没有大写,直接抛出异常,程序终止
super(Mymeta,self).__init__(class_name,class_bases,class_dict) class chinese(metaclass=Mymeta): # 首字母没有大写 # 元类是 Mymeta
pass # 执行结果:
# 直接报错
# TypeError: 类名首字母必须大写

自定义元类控制类的行为示例2:(类体中需要有注释且注释不能为空)

补充知识: 类的名称空间中有一个key 是 “__doc__”,它的含义是“类里面的注释”

class Mymeta(type):
def __init__(self,class_name,class_base,class_dict):
if "__doc__" not in class_dict or not class_dict["__doc__"].strip(): # class_dict 中没有"__doc__"这个key 或者 class_dict["__doc__"]为空 # 空字符串和None自带bool值为False
raise TypeError("必须有注释,且注释不能为空") super().__init__(class_name,class_base,class_dict) class chinese(metaclass=Mymeta):
pass # 运行结果:
# 报错
# TypeError: 必须有注释,且注释不能为空

自定义元类控制类的实例化行为:

补充知识点: __call__ 方法: 让对象(如 people)也能有 people()的方法。(此方法是给对象用的)

class Foo:
def __call__(self, *args, **kwargs): # 能让对象变成一个可调用对象 # obj()能触发 __call__
print(self)
print(args)
print(kwargs) obj = Foo() # 同理,Foo()能这样调用, Foo的那个元类(type)里面也有 __call__ 方法
obj(1,2,3,a="A",b="AB") # obj也能用类似于类Foo()的方法 # 运行结果:
# <__main__.Foo object at 0x000000D889D0A240>
# (1, 2, 3)
# {'a': 'A', 'b': 'AB'} """
元类type内部也应该有一个 __call__ 方法, 会在调用Foo时触发执行
Foo(1,2,x=1) 即相当于 Foo.__call__(Foo,1,2,x=1) 同时,类的调用也是一个实例化的过程
"""

自定义元类控制类的实例化:

class Mymeta(type):
def __init__(self,class_name,class_bases,class_dict):
if not class_name.istitle():
raise TypeError("类的首字母必须大写")
if "__doc__" not in class_dict or not class_dict["__doc__"].strip():
raise TypeError("类中必须有注释且注释不能为空")
     super(Mymeta,self).__init__(class_name,class_bases,class_dict) """现在我自己定义一个__call__ 方法"""
def __call__(self, *args, **kwargs):
print("=====>>") class Chinese(object,metaclass=Mymeta):
"""
xx
"""
def __init__(self,name,age):
self.name = name
self.age = age def talk(self):
print("%s is talking" %self.name) # obj = Chinese("neo",22) # Chinese能这样调用说明Mymeta中默认有 __call__ 方法
obj = Chinese("neo",22) # Chinese("neo",22) 在实例化的过程中会自动调用我重新定义的__call__ , 即 Chinese.__call__(Chinese,"neo",22)
print(obj)
# 运行结果:
# =====>>
# None """
正常情况下,类实例化时会有3个过程发生:
1. 先造出一个空对象
2. 根据__init__把空对象初始化
3. 得到一个返回值
"""
# 所以我上面在Mymeta中自定义的__call__ 是不规范的

下面对 Mymeta 中的 __call__ 方法进行修改,并说明类在实例化时自动调用其__init__方法的机制:

class Mymeta(type):
def __init__(self,class_name,class_bases,class_dict): # 这个__init__ 控制的是 类(Mymeta)的实例化
if not class_name.istitle():
raise TypeError("类的首字母必须大写")
if "__doc__" not in class_dict or not class_dict["__doc__"].strip():
raise TypeError("类中必须有注释且注释不能为空")
super(Mymeta,self).__init__(class_name,class_bases,class_dict) def __call__(self, *args, **kwargs): # 它控制的是类(Chinese)的调用
print("=====>>")
# 第一步: 先造一个空对象obj
obj = object.__new__(self) # object.__new__(cls)的作用是新建一个 cls类 的对象
# 第二步: 把空对象obj初始化
self.__init__(obj,*args,**kwargs) # Chinese调用自己的 __init__ # 把新建的obj这个对象传入到 Chinese类的__init__ 中 # 这行的 *args和**kwargs的含义是: __call__中的*args和**kwargs是怎么接收的,就怎么原封不动的传给这行代码中的*args和*kwargs
# 第三步: 返回obj
return obj
"""
以上也是类(Chinese)在实例化时__init__被触发自动动用的原因,因为 __call__ 被自动调用,而 __call__ 中又手动调用了 类(Chinese)中的 __init__
""" class Chinese(object,metaclass=Mymeta):
"""
Chinese中的 __init__ 在被 Mymeta 中的 __call__ 调用时,*args中的"neo"当做位置参数传给 name,**kwargs中的 age=22 当作关键字参数传给 age
"""
def __init__(self,name,age):
self.name = name
self.age = age def talk(self):
print("%s is talking" %self.name) obj = Chinese("neo",age=22)
print(obj.__dict__)
# 运行结果:
# =====>>
# {'name': 'neo', 'age': 22}

自定义元类控制类的实例化行为的应用:

单例模式: 如果生成的多个对象里面的属性完全一样,就没必要实例化多次,让这多个对象使用同一块内存空间就行

单例模式实现方式一 :

"""单例模式:让实例化的的对象共用同一个内存地址"""

class Mysql:
__instance = None # 设置一个隐藏属性,用于储存实例化的对象
def __init__(self):
self.host = "127.0.0.1"
self.port = 3306 @classmethod
def singleton(cls):
if not cls.__instance: # __instance 为 None时,说明还没有实例化过
obj = cls() # 实例化得到一个对象
cls.__instance = obj # 把实例化的对象赋值给 __instance
return cls.__instance def con(self):
pass def execute(self):
pass obj1 = Mysql.singleton()
obj2 = Mysql.singleton()
obj3 = Mysql.singleton() print(obj1)
print(obj2)
print(obj3) # 运行结果:
# <__main__.Mysql object at 0x000000E52BF366D8>
# <__main__.Mysql object at 0x000000E52BF366D8>
# <__main__.Mysql object at 0x000000E52BF366D8>

单例模式实现方式二: 元类的方式

"""通过自定义元类控制类的实例化过程:单例模式的 __call__ 方法"""

class Mymeta(type):
__instance = None # 定义一个隐藏属性用于储存实例化的对象
def __init__(self,class_name,class_bases,class_dict):
if not class_name.istitle():
raise TypeError("首字母必须大写,且只能首字母大写")
super().__init__(class_name,class_bases,class_dict) def __call__(self, *args, **kwargs):
if not self.__instance: # 如果没有经过实例化
obj = object.__new__(self) # 创建一个 self 的新对象
self.__init__(obj) # 对新创建的 self的对象执行 Mysql中的 __init__ 方法(实例化)
self.__instance = obj # 把实例化后的对象赋值给 __instance
return self.__instance class Mysql(metaclass=Mymeta):
def __init__(self):
self.host = "127.0.0.1"
self.port = 3306 def con(self):
pass
def execute(self):
pass obj1 = Mysql()
obj2 = Mysql()
obj3 = Mysql() print(obj1)
print(obj2)
print(obj3)
# 运行结果:
# <__main__.Mysql object at 0x0000001965AC67B8>
# <__main__.Mysql object at 0x0000001965AC67B8>
# <__main__.Mysql object at 0x0000001965AC67B8>

单例类:

class Singleton:
__instance = None def __init__(self):
pass def __new__(cls, *args, **kwargs):
if not cls.__instance:
cls.__instance = object.__new__(cls)
return cls.__instance
a = Singleton()
b = Singleton() print(a)
print(b) # __new__()方法是在实例化之前被调用,返回一个空对象;然后__init__()方法对这个空对象添加独有属性(__init__没有返回值);
# __new__() 是一个静态方法(@staticmethod)

练习: 加工标准类型,实现一个列表,所有元素都是字符串,实现获取中间值的属性,实现授权清空元素。

class MyList(list):
def __init__(self,*args,auth=True):
self.auth = auth
for i in args:
if not isinstance(i,str):
raise TypeError("only string is valid")
super().__init__(args) @property
def show_mid(self):
length = len(self)
half_length = length/2 # 相除的结果是float型 if length%2 == 0:
return [self[int(half_length)-1],self[int(half_length)]]
else:
return self[int(half_length)]
def clear(self):
if self.auth:
super().clear()
else:
print("no auth to clear list") s = MyList("neo","alex","egon","jack",auth=False)
print(s.show_mid)
s.clear()
print(s)

异常处理:

异常:就是程序运行时发生错误的信号(在程序出现错误时,则会产生一个异常,若程序没有处理它,则会抛出该异常,程序的运行也随之终止),在python中,错误触发的异常如下:

错误分两种:

  1. 语法错误: 程序在执行之前python会先检测其语法,如果出现语法错误,根本过不了python解释器的语法检测,所以必须在程序执行之前就改正

  2. 逻辑错误:如:TypeError、ValueError、NameError、IndexError、KeyError、AttributeError、StopIteration等

关于异常:

  一、 错误发生的条件如果是可以预知的,此时应该用if判断去预防异常

  二、 错误发生的条件如果是不可预知的,此时应该用异常处理机制:try ... except...

try:  # 检测下面的代码
f = open("a.txt","r")
print(f.__next__(),end="")
print(f.__next__(),end="")
print(f.__next__(),end="")
print(f.__next__(),end="")
print(f.__next__(),end="") f.close()
except StopIteration: # 捕捉异常类型,如果上面代码的出错类型和except后面的类型一样,则执行下面的代码,程序也继续运行;但被检测的代码(try里面的代码)在抛出异常后不再往下执行
print("\n出错了") print("====>>1")
print("====>>2") # 运行结果:
#
#
#
# 出错了
# ====>>1
# ====>>2

try ... except ...的详细用法:

  1. 异常类只能用来处理 指定的异常情况,如果非指定异常则无法处理

  2. 多分支:被检测的代码块抛出的异常有多种可能性,并且我们需要针对每一种异常类型都定制专门的处理逻辑

try:
print("===>1")
name
print("===>2")
l = [1,2,3]
l[100]
print("===>3")
d = {}
d["name"]
print("===>4") except KeyError as e: # 把异常值赋值给一个变量e
print("--->",e)
except IndexError as e:
print("--->",e)
except NameError as e:
print("--->",e) print("test") # 运行结果:
# ===>1
# ---> name 'name' is not defined
# test """多分支的except有点类似于 else的用法"""

  3. 万能异常: Exception(不包括语法错误); 使用场景: 被检测的代码块抛出的异常有多种可能性,并且我们针对所有的异常类型都只用一种处理逻辑

把多分支的代码简写:

try:
print("===>1")
name
print("===>2")
l = [1,2,3]
l[100]
print("===>3")
d = {}
d["name"]
print("===>4")
except Exception as e:
print("异常发生啦:",e) print("===> after code")
# 运行结果:
# ===>1
# 异常发生啦: name 'name' is not defined
# ===> after code

多分支和万能异常也能结合起来使用:

try:
print("===>1")
name
print("===>2")
l = [1,2,3]
l[100]
print("===>3")
d = {}
d["name"]
print("===>4")
except NameError as e:
print("--->",e)
except IndexError as e:
print("--->",e)
except KeyError as e:
print("--->",e)
except Exception as e: # 用法有点类似于 elif..., elif..., elif..., else...
print("其他异常",e) print("test after code") # 运行结果:
# ===>1
# ---> name 'name' is not defined
# test after code

  4. 其他结构:try ... except ... else...finally...

try:
print("===>1")
name = "NEO"
print("===>2")
l = [1,2,3]
val = l[1]
print("===>3")
d = {"name":"neo"}
d_name = d["name"]
print("===>4")
except NameError as e:
print("--->",e)
except IndexError as e:
print("--->",e)
except KeyError as e:
print("--->",e)
except Exception as e:
print("其他异常",e)
else: # 在被检测的代码块没有发生异常时执行else语句 # try ...except ... else ...中else的用法类似于 for 循环 else...
print("在被检测的代码块没有发生异常时执行else语句") finally: # 不管被检测的代码块有没有发生异常都会执行 finally语句 # finally语句属于被检测代码块的附加逻辑,即 finally语句隶属于被检测的代码块
print("不管被检测的代码块有没有发生异常都会执行 finally语句")
"""finally语句通常在一些关于回收资源方面使用,如 finally: f.close()""" print("test after code")
# 运行结果:
# ===>1
# ===>2
# ===>3
# ===>4
# 在被检测的代码块没有发生异常时执行else语句
# 不管被检测的代码块有没有发生异常都会执行 finally语句
# test after code

  5. 主动触发异常: raise 异常类型(值)

class People:
def __init__(self,name,age):
if not isinstance(name,str):
raise TypeError("name必须是str类型")
if not isinstance(age,int):
raise TypeError("age 必须是int 类型")
     self.name = name
     self.age = age people = People("neo",22)

  6. 自定义异常类型

class MyException(BaseException):
def __init__(self,msg):
super(MyException,self).__init__()
self.msg = msg def __str__(self): # 需要调用 __str__
return "<%s>" %self.msg # 需要return raise MyException("我自己的异常类型") # raise的作用除了 追踪异常信息和异常类型 还有 print(obj)(打印异常对象)的作用,所以需要在MyException中调用 __str__ # 运行结果:
# Traceback (most recent call last):
# File "F:/l面向对象.py", line 1860, in <module>
# raise MyException("我自己的异常类型")
# __main__.MyException: <我自己的异常类型>

  7. 断言assert

assert ("name" in info) and ("age" in info)  # 断言 后面的内容; 如果assert后面的内容为False,则报错:AssertionError

面向对象:元类、异常处理(try...except...)的更多相关文章

  1. Python之面向对象元类

    Python之面向对象元类 call方法: class People: def __init__(self,name): self.name=name # def __call__(self, *ar ...

  2. python中面向对象元类的自定义用法

    面向对象中的常用方法 1.instance 和 issubclass instance :判断两个对象是不是一类 issubclass :判断某个类是不是另一个类的子类 #两个常用方法的使用 clas ...

  3. python面向对象--元类

    一个类没有声明自己的元类,默认他的元类就是type,除了使用内置元类type,我们也可以通过继承type来自定义元类,然后使用metaclass关键字参数为一个类指定元类 class Foo: def ...

  4. Python面向对象篇之元类,附Django Model核心原理

    关于元类,我写过一篇,如果你只是了解元类,看下面这一篇就足够了. Python面向对象之类的方法和属性 本篇是深度解剖,如果你觉得元类用不到,呵呵,那是因为你不了解Django. 在Python中有一 ...

  5. day26 面向对象的常用方法 和 元类的使用

    1. 面向对象中的常用方法 ***** isinstance() # 判断某个对象是不是某个类的实例 # 判断stu对象是不是Student类的实例 是不是子类 issubclass() class ...

  6. Python全栈开发之9、面向对象、元类以及单例

    前面一系列博文讲解的都是面向过程的编程,如今是时候来一波面向对象的讲解了 一.简介 面向对象编程是一种编程方式,使用 “类” 和 “对象” 来实现,所以,面向对象编程其实就是对 “类” 和 “对象” ...

  7. python面向对象( item系列,__enter__ 和__exit__,__call__方法,元类)

    python面向对象进阶(下)   item系列 __slots__方法 __next__ 和 __iter__实现迭代器  析构函数 上下文管理协议 元类一.item系列 把对象操作属性模拟成字典的 ...

  8. 【转】python面向对象中的元类

    type() 动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的. 比方说我们要定义一个Hello的class,就写一个hello.py模块: class Hel ...

  9. Python异常处理及元类

    一.异常处理 异常是错误发生的信号,一旦程序出错就会产生一个异常,如果该异常没有被应用程序处理,那么该异常就会跑出来,程序的执行也随之终止,也就是说异常就是一个事件,该事件会在程序执行过程中发生,影响 ...

随机推荐

  1. Linux命令(009) -- tar

    tar命令可以为Linux的文件和目录创建档案(备份).利用该命令,可以为某一特定文件创建备份,也可以在档案中改变文件或向档案中加入新的文件:可以把一大堆的文件和目录全部打包成一个文件,这对于备份文件 ...

  2. 可以装一把——c#中手动添加控件

    TextBox txt = new TextBox(); //文本框控件 //如果想在移动控件位置 point(x,y) txt.Location = new Point(50,50); this.C ...

  3. iOS Programming Dynamic Type 1

    iOS Programming Dynamic Type 1  Dynamic Type is a technology introduced in iOS 7 that helps realize ...

  4. 【译】x86程序员手册32-9.4 中断描述符表

    9.4 Interrupt Descriptor Table 中断描述符表 The interrupt descriptor table (IDT) associates each interrupt ...

  5. Vuex/Vue 练手项目 在线汇率转换器

    详情请点击: https://zhuanlan.zhihu.com/p/33362758

  6. UVALive 4128 Steam Roller 蒸汽式压路机(最短路,变形) WA中。。。。。

    题意: 给一个由n*m个正方形格子组成的矩形,其中每个格子的边都是可以走的,长度给定,规定:如果在进入该路前需要拐弯,或者走完该路需要拐弯,都是需要付出双倍距离的(每条路最多算2倍).问从起点到终点的 ...

  7. adobe开发软件激活

    稳定支持至2017版本系列的adobe开发软件破解激活 本内容属原创,转载请注明出处!   以激活AE CC2017为例演示: 第一步打开软件第二步在产品列表中选择你所安装的产品(注意区分 32 位和 ...

  8. CentOS 7 安装Oracle VirtualBox

    1. 下载VirtualBox的repo文件: 登陆 https://www.virtualbox.org/wiki/Linux_Downloads 在网页的最下端的repo链接上右键下载,或者wge ...

  9. [安卓开发板]迅为IMX6 四核Android开发板

    工业级核心板-Android开发板 10层高速PCB设计,充分保证电磁兼容 处理器:开发板默认是四核商业扩展级芯片,可根据用户需求更换单核.双核.工业级.汽车级处理器,批量更省成本. 扩展引脚:320 ...

  10. css3 平行四边形 、大括弧

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...