一、类的装饰器

类作为一个对象,也可以被装饰。

例子

  def wrap(obj):
print("装饰器-----")
obj.x = 1
obj.y = 3
obj.z = 5
return obj

@wrap #将Foo类作为一个参数传入装饰器函数wrap,返回同时返回该对象,把新对象重新命名为Foo
#即 Foo = wrap(Foo)
class Foo:
pass

#执行结果:
#装饰器-----

print(Foo.__dict__) #输出结果可以看到,新的Foo类新增了x,y,z属性

  

函数可以作为一个对象,也有__dict__方法

  def wrap(obj):
print("装饰器-----")
obj.x = 1
obj.y = 3
obj.z = 5
return obj

@wrap #test = wrap(test)
def test():
print("test-----")
test.x = 10 #test的x属性被重新赋值
print(test.__dict__) #输出结果可以看到,test作为一个函数也有__dict__方法,
# 新的test函数新增了x,y,z属性

  

类的装饰器应用

例子

  class Type:

def __init__(self,key,except_type): #People对象的key,和期望的数据类型
self.key = key
self.except_type = except_type

def __get__(self, instance, owner):
return isinstance.__dict__[self.key]

def __set__(self, instance, value):
print("instance---",instance)
if not isinstance(value,self.except_type):
print("您输入的类型不是%s"%self.except_type)
raise TypeError
instance.__dict__[self.key] = value

def __delete__(self, instance):
isinstance.__dict__.pop(self.key)

def deco(**kwargs):
def wrapper(obj): #类的装饰器
for key,val in kwargs.items():
setattr(obj,key,Type(key,val)) #设置people类对象的每个参数的描述符
return obj
return wrapper

@deco(name=str,age=int)
class People:

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


p = People("nick",18)
print(p.__dict__)

  

二、自定义property

装饰器也可以是一个类,在自定义property中要使用一个类作为装饰器

例子

  class LazyProperty:

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

def __get__(self, instance, owner):
print("执行__get__")
if not instance: # 如果是用原类.属性来调用,,这时instance(对象)值为None,直接返回描述符对象
return self
res = self.func(instance) # 执行传入的函数属性,并把原对象作为参数传入
return res


class Room:

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

@LazyProperty # 这里相当于执行了area = LazyProperty(area),这里的azyProperty(area)其实是非数据描述符,
# 新的area已经是经过类LazyProperty装饰过的函数地址
def area(self):
return self.length * self.width


r = Room("nick", 18, 10)
print(r.area) # 执行对象的方法,先在对象的属性字典里寻找,没有则在非数据描述符里寻找,找到非数据描述符里的__get__方法。

  

实现延迟计算功能,即实现计算一次再次调用不再进行计算

  
  class LazyProperty:

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

def __get__(self, instance, owner):
print("执行__get__")
if not instance: # 如果是用原类.属性来调用,,这时instance(对象)值为None,直接返回描述符对象
return self
value = self.func(instance) # 执行传入的函数属性,并把原对象作为参数传入
setattr(instance,self.func.__name__,value) #将每次调用的函数属性名字和值存入对象的__dict__,
# self.func.__name__是获取被调用函数属性的名字
return value


class Room:

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

@LazyProperty # 这里相当于执行了area = LazyProperty(area),这里的azyProperty(area)其实是非数据描述符,
# 新的area已经是经过类LazyProperty装饰过的函数地址
def area(self):
return self.length * self.width


r = Room("nick", 18, 10)
print(r.__dict__)
print(r.area) # 执行对象的方法,先在对象的属性字典里寻找,没有则在非数据描述符里寻找,找到非数据描述符里的__get__方法。
print(r.__dict__)

  

三、property补充

一个静态属性property本质就是实现了get,set,delete三种方法

用语法糖可以实现property的类似属性的设置和删除,与一般的属性设置删除没有区别

  class People:

def __init__(self):
self.study = "8h"

@property
def study(self):
print("获取study,执行描述符的__get__方法")
return self.val
# return self.study #无线递归

@study.setter
def study(self,value):
print("执行__set__方法")
self.val = value

@study.deleter
def study(self):
print("执行__delete__方法")
del self.val

p = People()
print(p.study) #获取对象的study属性 ,self.study实际上是存在self.val里
p.study = "10h" #执行property描述符的__set__方法,设置对象的属性,
print(p.__dict__)
del p.study #执行property描述符的__delete_方法,删除对象的属性
print(p.__dict__)

  

四、元类

exec()函数

exec:三个参数

参数一:字符串形式的命令

参数二:全局作用域(字典形式),如果不指定,默认就是用全局 globals()

参数三:局部作用域(字典形式),如果不指定,默认就是用局部 locals()

exec会在指定的局部作用域内执行字符串内的代码,除非明确地使用global关键字

例子

  
  g = {"x":1,"y":2}
l = {"a":100}

exec("""
global x,y
x = 10
y = 100
z = 100
""",g,l) # exec 当成一个函数的执行,需要指定全局作用域和局部作用域
print(g) #在输出结果中可以看到x,y的值发生了变化
print(l) #新增加的作为局部作用域的属性

  

定义类的两种方式

(1)用class关键字定义类

  定义类的方式一:用class关键字定义
  class People:

country = "china"
def __init__(self,name):
self.name = name

def talk(self):
print("在说话")

  

 

(2)手动模拟class创建类的过程:将创建类的步骤拆分开,手动去创建

准备工作:

创建类主要分为三部分

  a 类名

  b 类的父类

  c 类体

  
  class_name = "People"  #设置类名
class_bases = (object,) #设置类的父类
class_body = """
country = "china"
def __init__(self,name):
self.name = name

def talk(self):
print("在说话")

"""
class_dic = {}
exec(class_body,globals(),class_dic) #这样执行一下得到类的名称空间(属性字典)
print(class_dic)
People2 = type(class_name,class_bases,class_dic) #用元类创建了类

  

什么是元类?

在python中,一切皆对象,一般的类也是一个类的对象,即这种起源、开始的类就称作元类。

用class关键字定义的类本身也是一个对象,负责产生该对象的类称之为元类(元类可以简称为类的类),内置的元类为type。

元类的参数

元类实例化创建一般的类有三个参数

1、类名class_name="xxx"

2、基类们class_bases=(object,),要继承的父类名,用元组

3、类的名称空间class_dic,类的名称空间是执行类体代码而得到的,就是类的属性字典

调用type时会依次传入以上三个参数

例子

  
  class Foo: #一般的用class 关键字创建的类
pass

f = Foo()
print(Foo)
print(Foo.__dict__)

t = type("FFo",(object,),{}) #由元类创造出来的一般类
print(t)
print(t.__dict__)

  

输出结果

  <class '__main__.Foo'>
{'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None}
<class '__main__.FFo'>
{'__module__': '__main__', '__dict__': <attribute '__dict__' of 'FFo' objects>, '__weakref__': <attribute '__weakref__' of 'FFo' objects>, '__doc__': None}

  

元类创建类也可以添加属性和方法

例子2

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


f = Foo("nick")
print(Foo)
print(Foo.__dict__)


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

t = type("FFo",(object,),{"__init__":__init,"x":1}) #可以直接在属性字典里为创建的类添加属性和方法
print(t)
print(t.__dict__)
t=T("nick")
print(t.__dict__)

  

输出结果

  <class '__main__.Foo'>
{'__module__': '__main__', 'x': 1, '__init__': <function Foo.__init__ at 0x00665DB0>, '__dict__': <attribute '__dict__' of 'Foo' objects>, '__weakref__': <attribute '__weakref__' of 'Foo' objects>, '__doc__': None}
<class '__main__.FFo'>
{'__init__': <function __init at 0x00665DF8>, 'x': 1, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'FFo' objects>, '__weakref__': <attribute '__weakref__' of 'FFo' objects>, '__doc__': None}
{'name': 'nick'}

  

自定义元类

一个类没有声明自己的元类,默认它的元类就是type,除了使用元类type,用户也可以通过继承type来自定义元类

例子1

  class MyType(type):

def __init__(self,class_name,class_bases,class_dic):
print("类名",class_name)
print("基类",class_bases)
print("类字典",class_dic)
super().__init__(class_name,class_bases,class_dic)


class People(object,metaclass=MyType): #这里metaclass = MyType就执行MyType("People",(ovject,),{}),
# 然后自动实例化调用MyType的__init__方法
def __init__(self,name,age):
self.name = name
self.age = age

def talk(self):
print("在说话。。。")

p = People("nick",18)
print(p.__dict__)

  

输出结果

  
  类名 People
基类 (<class 'object'>,)
类字典 {'__module__': '__main__', '__qualname__': 'People', '__init__': <function People.__init__ at 0x00625DF8>, 'talk': <function People.talk at 0x00625DB0>}
{'name': 'nick', 'age': 18}

  

自定义元类控制类的创建

例子1

对新建的类名的要求

  
  class MyMeta(type):

def __init__(self,class_name,class_bases,class_dic):
if not class_name.istitle(): #判断产生的类的首字母是不是大写
raise TypeError("类的首字母不是大写")
super().__init__(class_name,class_bases,class_dic)

class people(metaclass=MyMeta): #程序运行到这里会直接报错

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

def talk(self):
print("在说话")

  

例子2

要求新建的类必须要有注释

  
  class MyMeta(type):

def __init__(self,class_name,class_bases,class_dic):
print(class_dic["__doc__"],bool(class_dic["__doc__"]))
if not class_dic["__doc__"].strip() or "__doc__" not in class_dic : #判断新建类没有注释或者注释是空的
raise TypeError("新建类没有注释")

super().__init__(class_name,class_bases,class_dic)

class people(metaclass=MyMeta): #程序运行到这里会直接报错
"""
"""
def __init__(self,name,age):
self.name = name
self.age = age

def talk(self):
print("在说话")

  

自定义元类控制类的实例化过程

自定义元类创建的类的对象实例化过程

例子

  
  class MyType(type):

def __init__(self,class_name,class_bases,class_dic):
print("类名",class_name)
print("基类",class_bases)
print("类字典",class_dic)
super().__init__(class_name,class_bases,class_dic)

def __call__(self, *args, **kwargs):
print("self----",self)
obj = object.__new__(self) #创建一个新的对象,这里就是创建People类的对象,一个实例化的过程
self.__init__(obj,*args,**kwargs) #执行对象的__init__方法,给对象属性字典加属性,这里的self是People类
return obj

class People(object,metaclass=MyType): #这里metaclass = MyType就执行MyType("People",(ovject,),{}),
# 然后自动实例化调用MyType的__init__方法,生成一个people类
def __init__(self,name,age):
self.name = name
self.age = age

def talk(self):
print("在说话。。。")
print("实例化之前")
p = People("nick",18) #这里触发了__call__方法,自己的类里没有call方法就找元类,执行元类的call方法
#执行call方法之后得到新的对象赋值给p,
print(p.__dict__)
print(p.name)

  

- 创建类时,先执行type的__init__。
- 类的实例化时,执行type的__call__,__call__方法的的返回值就是实例化的对象。

__call__内部调用:
- 类.__new__,创建对象
- 类.__init__,对象的初始化

单例模式

单例:即单个实例,指的是同一个类实例化多次的结果指向同一个对象,用于节省内存空间

单例模式主要是优化内存,无论你实例化多少次,始终用同一个对象

例子1

如果我们从配置文件中读取配置来进行实例化,在配置相同的情况下,就没必要重复产生对象浪费内存了

创建单例模式的方式1

  
  #用类实现单例模式

class MySQL():

__instance = None #先定义一个空

def __init__(self):
self.host = "127.0.0.1"
self.port = "8090"

@classmethod
def singleton(cls):
if not cls.__instance: #如果之前没有实例则第一次创建一个实例对象,之后如果有实例则直接返回该实例对象
obj = cls()
cls.__instance = obj #将第一次创建的对象赋值给类属性,方便下次调用
return cls.__instance

obj1 = MySQL.singleton() #创建新的对象
obj2 = MySQL.singleton()
print(obj1 is obj2)

  

创建单例模式的方式2,用元类创建单例模式

  #第二种方式,用元类实现单例模式
  class Mymeta(type):

def __init__(self,class_name,class_bases,class_dic):
super().__init__(class_name,class_bases,class_dic)
self.__instance = None #这里的self是根据这个元类创建的类,也可以把这个创建的类暂时理解为类的对象

def __call__(self, *args, **kwargs):
if not self.__instance:
obj = object.__new__(self) #用__new__方法创建类的对象
self.__init__(obj,*args, **kwargs) #运行类的__init__方法,为新建对象的属性字典赋值
self.__instance = obj #第一次创建对象时将类(self)的属性重新赋值为刚创建的obj
return self.__instance


class Mysql(metaclass=Mymeta):

def __init__(self):
self.host = "127.0.0.1"
self.port = 8090

obj3 = Mysql() # 创建类的对象,调用元类的__call__方法
obj4 = Mysql()
print(obj3 is obj4)

  

 

 

Python之路(第二十八篇) 面向对象进阶:类的装饰器、元类的更多相关文章

  1. Python之路(第二十九篇) 面向对象进阶:内置方法补充、异常处理

    一.__new__方法 __init__()是初始化方法,__new__()方法是构造方法,创建一个新的对象 实例化对象的时候,调用__init__()初始化之前,先调用了__new__()方法 __ ...

  2. Python之路(第二十六篇) 面向对象进阶:内置方法

    一.__getattribute__ object.__getattribute__(self, name) 无条件被调用,通过实例访问属性.如果class中定义了__getattr__(),则__g ...

  3. Python之路(第二十四篇) 面向对象初级:多态、封装

    一.多态 多态 多态:一类事物有多种形态,同一种事物的多种形态,动物分为鸡类,猪类.狗类 例子 import abc class H2o(metaclass=abc.ABCMeta): ​ def _ ...

  4. Python之路(第二十五篇) 面向对象初级:反射、内置方法

    [TOC] 一.反射 反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问.检测和修改它本身状态或行为的一种能力(自省).这一概念的提出很快引发了计算机科学领域关于应用反射性的研究.它 ...

  5. Python之路(第二十二篇) 面向对象初级:概念、类属性

    一.面向对象概念 1. "面向对象(OOP)"是什么? 简单点说,“面向对象”是一种编程范式,而编程范式是按照不同的编程特点总结出来的编程方式.俗话说,条条大路通罗马,也就说我们使 ...

  6. Python之路(第十八篇)shutil 模块、zipfile模块、configparser模块

    一.shutil 模块 1.shutil.copyfileobj(fsrc, fdst[, length]) 将文件内容拷贝到另一个文件中,需要打开文件 import shutil shutil.co ...

  7. Python开发【第二十二篇】:Web框架之Django【进阶】

    Python开发[第二十二篇]:Web框架之Django[进阶]   猛击这里:http://www.cnblogs.com/wupeiqi/articles/5246483.html 博客园 首页 ...

  8. Python之路【第八篇】:堡垒机实例以及数据库操作

    Python之路[第八篇]:堡垒机实例以及数据库操作   堡垒机前戏 开发堡垒机之前,先来学习Python的paramiko模块,该模块机遇SSH用于连接远程服务器并执行相关操作 SSHClient ...

  9. Android UI开发第二十八篇——Fragment中使用左右滑动菜单

    Fragment实现了Android UI的分片管理,尤其在平板开发中,好处多多.这一篇将借助Android UI开发第二十六篇——Fragment间的通信. Android UI开发第二十七篇——实 ...

随机推荐

  1. NPOI导出excel(2.0.6版本)

    public static void WriteExcel(System.Data.DataTable dt,string fileName) { NPOI.XSSF.UserModel.XSSFWo ...

  2. inode引起的Linux无法创建新文件,磁盘空间不足

    df -h,判断硬盘空间是否已经满了,占用率达100% ,就可以断定该分区满了. df -ia,占用率达100%,也会导致无法创建新文件.一般都是存在大量小文件引起的. inode包含文件的元信息,具 ...

  3. Bootstrap 前端UI框架

    Bootstrap 有哪些优越性? 1.简单灵活的用于搭建WEB页面的HTML,CSS, JavaScript的工具集 2.基于html5, css3, 具有良好特性,友好的学习曲线,卓越的兼容性,1 ...

  4. sed -i命令详解

    [root@www ~]# sed [-nefr] [动作] 选项与参数: -n :使用安静(silent)模式.在一般 sed 的用法中,所有来自 STDIN 的数据一般都会被列出到终端上.但如果加 ...

  5. mysqldump备份与恢复笔记

    mysql> show databases; +--------------------+ | Database           | +--------------------+ | inf ...

  6. 第26课 可变参数模板(7)_any和variant类的实现

    1. any类的实现 (1)any类: ①是一个特殊的,只能容纳一个元素的容器,它可以擦除类型,可以将何任类型的值赋值给它. ②使用时,需要根据实际类型将any对象转换为实际的对象. (2)实现any ...

  7. k8s学习笔记之八:存储卷

    第一章.前言 默认情况下容器的数据都是非持久化的, 在容器消亡以后数据也跟着丢失, 所以 Docker 提供了 Volume 机制以便将数据持久化存储. 类似的, Kubernetes 提供了更强大的 ...

  8. Java中线程池的实现原理

    知识点总结 ---------------------------------------------------------------------------------------------- ...

  9. SpringBoot 之 MVC

    SpringBoot MVC 和静态资源 首先,我们一定要搞清楚,mvc 配置和 static 配置的联系和区别. mvc 配置其实就是给 spring mvc 框架用的, 具体来说, 比如 @Req ...

  10. windows共享文件夹权限设置

    权限设置及更改,最好在右键属性里面, 在计算机管理,共享文件夹->共享里面修改,有时候会不生效. windows的凭据修改,在用户注销后才会生效.