面向对象终阶

元类

Python一切皆对象 Linux一切皆文件
定义: 元类是类的类,是类的模板。元类的实例为类,正如类的实例为对象。

类的本质是对象, 于是可以对类做如下的操作:

  1. 你可以将它赋值给一个变量
  2. 你可以拷⻉它
  3. 你可以为它增加属性
  4. 你可以将它作为函数参数进行传递

通过type函数动态创建类

type函数功能一: 判断对象的类型。
type函数功能二: 动态的创建类。type可以接受一个类的描述作为参数,然后返回一个类。
type函数语法: type(类名, 父类名称的元组, 属性信息)

def hello(self):
    print('hello')
Person1=type('Person',(object,),{'country':'China','hello':hello}) #动态创建类给Person1,类的名称、父类(写成元组)、类的属性与属性值、类的方法名与方法
p1=Person1() #实例化对象
print(p1.country)
p1.hello() 

执行结果:
China
hello

总结:
1). 元类就是创建类的类。函数type就是是元类。
2). Python中一切皆对象。包括整数、字符串、函数以及类都是对象,且都是从type类创建而来。
3). 动态生成类,不能控制类是如何生成的。 python3 的metaclass可动态创建类。
4). 很多Web框架都会使用metaclass 来创建类。掌握元类对理解源代码至关重要。eg: ORM框架类

自定义元类

metaclass的原理是什么呢? 应用场景: Django(ORM)、元类实现单例模式等。 元类本身而言,实现的功能是:

  1. 拦截类的创建
  2. 修改类
  3. 返回修改之后的类

自定义元类实现单例模式:指定创建Person类的元类是Singleton,使得Person类实例化对象只能有一个。

class Singleton(type): #Singleton用于创建实现单例模式的类Person
    """
    自定义元类实现单例模式,父类是type
    """
    cache={} #存储 类:类实例化对象即{'Person':person()}
    def __call__(cls):
        #判断Person是否实例化,有直接返回Person的对象,没有就找Singleto的父类创建一个对象返回给用户
        if cls not in cls.cache:
            cls.cache[cls]=super(Singleton, cls).__call__() #找父类创建一个对象
        return cls.cache[cls]#返回一个对象
class Person(object,metaclass=Singleton):#指定创建Person类的元类是Singleton,Singleton中没有new方法,因此寻找Singleton的父类type的new方法创建类Person,此时返回Person为对象,因此可以调用call方法.
    pass
p1=Person() #Person为type中的new方法返回的对象,Person()就会调用call方法
p2=Person()
print(p1,p2)

执行结果:
<__main__.Person object at 0x00000244D4A70D08> <__main__.Person object at 0x00000244D4A70D08>

抽象基类

抽象基类有两个特点:     
 1.规定继承类必须具有抽象基类指定的方法     
 2.抽象基类无法实例化   
 基于上述两个特点,抽象基类主要用于接口设计   
 实现抽象基类可以使用内置的abc模块
 
抽象基类理解:

import abc
class Human(metaclass=abc.ABCMeta):#当指定Human类的元类为abc.ABCMeta时,Human为一个抽象基类
    @abc.abstractmethod #抽象方法,加此装饰器表示继承类也必须要有相同的方法introduce
    def introduce(self):
        print('introducing..')
    def hello(self): #没有加抽象方法装饰器,继承类可以没有相同的方法hello
        print('hello')
class Person(Human):
    #规定继承类必须具有抽象基类指定的方法
    def introduce(self):
        print('person')
    def hello(self):
        print('person hello')
#抽象基类无法实例化
#h=Human() 报错:Can't instantiate abstract class Human with abstract methods hello, introduce
p=Person() #如果子类没有抽象基类指定的方法introduce就不能实例化对象:Can't instantiate abstract class Person with abstract methods introduce
p.introduce() #继承类有抽象方法introduce,子类必须要有introduce方法(方法体内可以不一样),没有会报错。
p.hello() #继承类有hello方法时执行继承类hello方法内容,继承类没有hello方法寻找父类Human的hello方法。

执行结果:
person
person hello

自省机制

检查某些事物以确定它是什么、它知道什么以及 它能做什么。自省向程序员提供了极大的灵活性和控制力。例如Python, Ruby, object-C, C++都有自省的能力,这里面的C++的自省的能力最弱, 只能够知道是什么类型,而像Python可以知道是什么类型,还有什么属性。
Python中比较常见的自省(introspection)机制(函数用法)有: dir()、type()、 hasattr(),、setattr()、getattr()、delattr()、isinstance(),通过这些函数,我们能够 在程序运行时得知对象的类型,判断对象是否存在某个属性,访问对象的属性。

__ slots __

动态语言与静态语言的不同?

  1. 动态语言:可以在运行的过程中,修改代码
  2. 静态语言:编译时已经确定好代码,运行过程中不能修改

如果我们想要限制实例的属性怎么办?

  1. Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性
  2. 使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的
#slot限制对象属性
import time
from datetime import  date
class Date(object):
    #slot限制该对象能添加的属性信息
    __slots__ =  '__year','__month','__day' #slot限定该对象只能有三个属性信息:'__year','__month','__day'
    def __init__(self, year,month,day):
        self.__year=year
        self.__month=month
        self.__day=day
    @property #只能查看属性不能修改
    def year(self):
        return self.__year
    @property
    def month(self):
        return self.__month
    @property
    def day(self):
        return self.__day
    @classmethod #类方法传入类名称
    def today(cls):
        time_t=time.localtime()
        return cls(time_t.tm_year,time_t.tm_mon,time_t.tm_mday)# Date(2019, 1, 5)实例化对象,传入3个参数给new方法
    def __str__(self):
        return '%s-%s-%s'%(self.__year,self.__month,self.__day)
d=Date(2019,10,10) #找Date的new方法创建对象,但是Date中没有new方法,就会找Date的父类object的new方法创建对象让Date的init方法接收
print('对象类型:',type(d))#type(d)返回类名,d由此类创建
print('判断是否有year属性:',hasattr(d,'year'))
print('判断是否有time属性:',hasattr(d,'time')) #当前没有time属性
# setattr(d,'time','10:10:10') #slot限制了属性的添加,因此不可以给d添加属性time
# print('time:',getattr(d,'time')) #无法找到d的time属性对应的值
print(Date.today())
#注意使用new方法的条件为:需要在实例化对象前执行一些操作时使用例如判断等.
#在实例化对象时如果Date中有new方法,就需要先实例化一个对象并返回这个对象.

执行结果:
对象类型: <class '__main__.Date'>
判断是否有year属性: True
判断是否有time属性: False
2020-1-6

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值