Python面向对象编程(三)

目录
  • 一、isinstance和issubclass
  • 二、反射(hasattr和getattr和setattr和delattr)
    • 1、反射在类中的使用
    • 2、反射在模块中的使用
    • 3、实例:基于反射机制模拟web框架路由
  • 三、__getattr__、__setattr__和__delattr__和__getattribute__事件
    • __getattribute__
  • 四、__setitem__和__getitem和__delitem__
  • 五、__format__:自定制格式化字符串
  • 六、__del__:析构方法
  • 七、__slots__
  • 八、__doc__:返回类的注释信息
  • 九、__call__:会在调用对象时自动触发。
  • 十、__init__和__new__:类构造器
  • 十一、__str__和__repr__
    • __str__:执行str函数或print函数触发
    • __repr__:执行repr函数或者交互式解释器触发
  • 十二、__module__和__class__
  • 十三、实现文件上下文管理(__enter__和__exit__)
    • 模拟open
  • 十四、描述符(__get__和__set__和__delete__)
    • 1、使用描述符
    • 2、类的装饰器:无参
    • 3、类的装饰器:有参
    • 4、描述符与类装饰器结合使用
    • 5、利用描述符原理自定制@property
    • 6、自定制@classmethod
    • 7、自定制@staticmethod
  • 十五、元类(metaclass)
    • 1、type实现
    • 2、自定义元类控制类
    • 3、自定义元类控制类的实例化
    • 4、练习:使用元类修改属性为隐藏属性
    • 5、利用元类实现单例模式

Python面向对象编程(一)

Python面向对象编程(二)

Python面向对象编程(三)

一、isinstance和issubclass

  • type():不会认为子类实例是一种父类类型;
  • isinstance():认为子类实例是一种父类类型。
  • issubclass():判断是否为其子类。
class Foo(object):
    pass

class Bar(Foo):
    pass

print(type(Foo()) == Foo)
# True

print(type(Bar()) == Foo)
# False

# isinstance参数为对象和类
print(isinstance(Bar(), Foo))
# True

print(issubclass(Bar, Foo))
# True

print(issubclass(Foo, object))
# True

二、反射(hasattr和getattr和setattr和delattr)

1、反射在类中的使用

反射就是通过字符串来操作类或者对象的属性。反射本质就是在使用内置函数,其中反射有以下四个内置函数:

  • hasattr:判断一个方法是否存在与这个类中
  • getattr:根据字符串去获取obj对象里的对应的方法的内存地址,加"()"括号即可执行
  • setattr:通过setattr将外部的一个函数绑定到实例中
  • delattr:删除一个实例或者类中的方法
class People:
    country = 'China'

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

    def eat(self):
        print('%s is eating' % self.name)

peo1 = People('nick')

print(hasattr(peo1, 'eat'))  # peo1.eat
# True

print(getattr(peo1, 'eat'))  # peo1.eat
# >

print(getattr(peo1, 'xxxxx', None))
# None

setattr(peo1, 'age', 18)  # peo1.age=18
print(peo1.age)
# 18

print(peo1.__dict__)
# {'name': 'egon', 'age': 18}

delattr(peo1, 'name')  # del peo1.name
print(peo1.__dict__)
# {'age': 18}

2、反射在模块中的使用

动态导入一个模块__import__,并且动态输入函数名然后执行相应功能。

注意:getattr,hasattr,setattr,delattr对模块的修改都在内存中进行,并不会影响文件中真实内容。

# dynamic.py
imp = input("请输入模块:")
commons = __import__(imp)  # 等价于import imp
# commons = __import__(imp, fromlist=True)  # 模块名可能不是在本级目录中存放着,改用这种方式就能导入成功
inp_func = input("请输入要执行的函数:")
f = getattr(commons, inp_func, None)  # 作用:从导入模块中找到你需要调用的函数inp_func,然后返回一个该函数的引用.没有找到就烦会None
f()  # 执行该函数

r = hasattr(commons, 'age')  # 判断某个函数或者变量是否存在
print(r)

setattr(commons, 'age', 18)  # 给commons模块增加一个全局变量age = 18,创建成功返回none

setattr(commons, 'age', lambda a: a + 1)  # 给模块添加一个函数

delattr(commons, 'age')  # 删除模块中某个变量或者函数

3、实例:基于反射机制模拟web框架路由

需求:比如我们输入<www.xxx.com/commons/f1> ,返回f1的结果。

# 动态导入模块,并执行其中函数
url = input("url: ")

target_host,target_module, target_func = url.split('/')
m = __import__('aaa.' + target_module, fromlist=True)

inp = url.split("/")[-1]  # 分割url,并取出url最后一个字符串
if hasattr(m, inp):  # 判断在commons模块中是否存在inp这个字符串
    inp= getattr(m, inp)  # 获取inp的引用
    inp()  # 执行
else:
    print("404")

三、__getattr__、__setattr__和__delattr__和__getattribute__事件

  • __getattr__:只有在使用点调用属性且属性不存在的时候才会触发。比较有用
  • __delattr__:删除属性的时候会触发
  • __setattr__:添加/修改属性会触发它的执行
    当你自己写__getattr__、__delattr__、__setattr__方法,系统会调用你写的方法,如果没写,系统调用默认
class Foo:
    x = 1

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

    def __getattr__(self, item):
        print('----> from getattr:你找的属性不存在')

    def __setattr__(self, key, value):
        print('----> from setattr')
        # self.key = value  # 这就无限递归了,你好好想想
        # self.__dict__[key] = value  # 应该使用它

    def __delattr__(self, item):
        print('----> from delattr')
        # del self.item  # 无限递归了
        self.__dict__.pop(item)

f1 = Foo(10)
# ----> from setattr
print(f1.__dict__ )  # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值。除非你直接操作属性字典,否则永远无法赋值
# {}

f1.z = 3
# ----> from setattr
print(f1.__dict__)
# {}

f1.__dict__['a'] = 3  # 我们可以直接修改属性字典,来完成添加/修改属性的操作(不会触发__setattr__)
del f1.a
# ----> from delattr

print(f1.__dict__)
# {}

__getattribute__

查找属性无论是否存在,都会执行。

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

    def __getattribute__(self, item):
        print('不管是否存在,我都会执行')

f1 = Foo(10)

f1.x
# 不管是否存在,我都会执行

f1.xxxxxx
# 不管是否存在,我都会执行

当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,除非__getattribute__在执行过程中抛出异常AttributeError

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

    def __getattr__(self, item):
        print('执行的是我')
        # return self.__dict__[item]

    def __getattribute__(self, item):
        print('不管是否存在,我都会执行')
        raise AttributeError('哈哈')

f1 = Foo(10)

f1.x
# 不管是否存在,我都会执行
# 执行的是我

f1.xxxxxx
# 不管是否存在,我都会执行
# 执行的是我

四、__setitem__和__getitem和__delitem__

  • __setitem__:中括号赋值时触发
  • __getitem__:中括号取值时触发
  • __delitem__:中括号删除时触发
  • __delattr__:.删除时触发
class Foo:
    def __init__(self, name):
        self.name = name

    def __getitem__(self, item):
        print('getitem执行', self.__dict__[item])

    def __setitem__(self, key, value):
        print('setitem执行')
        self.__dict__[key] = value

    def __delitem__(self, key):
        print('del obj[key]时,delitem执行')
        self.__dict__.pop(key)

    def __delattr__(self, item):
        print('del obj.key时,delattr执行')
        self.__dict__.pop(item)

f1 = Foo('sb')

f1['age'] = 18
# setitem执行

f1['age1'] = 19
# setitem执行

f1['age']
# getitem执行 18

f1['name'] = 'tank'
# setitem执行

del f1.age1
# del obj.key时,delattr执行

del f1['age']
# del obj[key]时,delitem执行

print(f1.__dict__)
# {'name': 'tank'}

五、__format__:自定制格式化字符串

date_dic = {
    'ymd': '{0.year}:{0.month}:{0.day}',
    'dmy': '{0.day}/{0.month}/{0.year}',
    'mdy': '{0.month}-{0.day}-{0.year}',
}

class Date:
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

    def __format__(self, format_spec):
        # 默认打印ymd的{0.year}:{0.month}:{0.day}格式
        if not format_spec or format_spec not in date_dic:
            format_spec = 'ymd'
        fmt = date_dic[format_spec]
        return fmt.format(self)

d1 = Date(2016, 12, 29)

print(format(d1))
# 2016:12:29

print('{:mdy}'.format(d1))
# 12-29-2016

六、__del__:析构方法

会在对象被删除之前自动触发

class People:
    def __init__(self, name, age):
        self.name = name
        self.age = age
        self.f = open('test.txt', 'w', encoding='utf-8')

    def __del__(self):
        print('run======>')
        # 做回收系统资源相关的事情
        self.f.close()

obj = People('egon', 18)

del obj  # del obj会间接删除f的内存占用,但是还需要自定制__del__删除文件的系统占用
# run=-====>

七、__slots__

使用点来访问属性本质就是在访问类或者对象的__dict__属性字典(类的字典是共享的,而每个实例的是独立的)。

__slots__是一个类变量,变量值可以是列表,元祖,或者可迭代对象,也可以是一个字符串(意味着所有实例只有一个数据属性)

字典会占用大量内存,如果你有一个属性很少的类,但是有很多实例,为了节省内存可以使用__slots__取代实例的__dict__。

class Foo:
    __slots__ = 'x'

f1 = Foo()
f1.x = 1
f1.y = 2  # 报错
print(f1.__slots__ )  # f1不再有__dict__

当你定义__slots__后,__slots__就会为实例使用一种更加紧凑的内部表示。使用__slots__后不能再给实例添加新的属性了,只能使用在__slots__中定义的那些属性名。

注意:__slots__的很多特性都依赖于普通的基于字典的实现。另外,定义了__slots__后的类不再支持一些普通类特性了,比如多继承。

大多数情况下,你应该只在那些经常被使用到的用作数据结构的类上定义__slots__比如在程序中需要创建某个类的几百万个实例对象 。

class Bar:
    __slots__ = ['x', 'y']

n = Bar()
n.x, n.y = 1, 2
n.z = 3  # 报错

八、__doc__:返回类的注释信息

class Foo:
    '我是描述信息'
    pass

print(Foo.__doc__)
# 我是描述信息

该属性无法被继承

class Foo:
    '我是描述信息'
    pass

class Bar(Foo):
    pass

print(Bar.__doc__)  # 该属性无法继承给子类
# None

九、__call__:会在调用对象时自动触发。

构造方法的执行是由创建对象触发的,即:对象 = 类名() ;而对于 __call__ 方法的执行是由对象后加括号触发的,即:对象() 或者 类()()

class Foo:
    def __init__(self):
        print('__init__触发了')

    def __call__(self, *args, **kwargs):
        print('__call__触发了')

obj = Foo()  # 执行 __init__
# __init__触发了

obj()  # 执行 __call__
# __call__

十、__init__和__new__:类构造器

__new__方法的第一个参数是这个类,而其余的参数会在调用成功后全部传递给__init__方法初始化。

__new__方法(第一个执行)先于__init__方法执行:

class A:
    pass

class B(A):
    def __new__(cls):
        print("__new__方法被执行")
        return super().__new__(cls)

    def __init__(self):
        print("__init__方法被执行")

b = B()

# __new__方法被执行
# __init__方法被执行

绝大多数情况下,我们都不需要自己重写__new__方法,但在当继承一个不可变的类型(例如str类,int类等)时,它的特性就尤显重要了。我们举下面这个例子:

# 1、使用init的情况:
class CapStr1(str):
    def __init__(self, string):
        string = string.upper()

a = CapStr1("I love China!")
print(a)
# I love China!   无变化 !!!!!!!

# 2、使用__new__的情况
class CapStr2(str):
    def __new__(cls, string):
        string = string.upper()
        return super().__new__(cls, string)

a = CapStr2("I love China!")
print(a)
# I LOVE CHINA!

十一、__str__和__repr__

__str__:执行str函数或print函数触发

class Foo:
    def __init__(self, name, age):
        """对象实例化的时候自动触发"""
        self.name = name
        self.age = age

    def __str__(self):
        print('打印的时候自动触发,但是其实不需要print即可打印')
        return f'{self.name}:{self.age}'  # 如果不返回字符串类型,则会报错

obj = Foo('nick', 18)
print(obj)  # obj.__str__() # 打印的时候就是在打印返回值
# 打印的时候自动触发,但是其实不需要print即可打印
# nick:18

__repr__:执行repr函数或者交互式解释器触发

  • 如果__str__没有被定义,那么就会使用__repr__来代替输出。
  • 注意:这俩方法的返回值必须是字符串,否则抛出异常。
class School:
    def __init__(self, name, addr, type):
        self.name = name
        self.addr = addr
        self.type = type

    def __repr__(self):
        return 'School(%s,%s)' % (self.name, self.addr)

    def __str__(self):
        return '(%s,%s)' % (self.name, self.addr)

s1 = School('oldboy1', '北京', '私立')
print('from repr: ', repr(s1))
# from repr:  School(oldboy1,北京)

print('from str: ', str(s1))
# from str:  (oldboy1,北京)

print(s1)
# (oldboy1,北京)

s1  # jupyter属于交互式
# School(oldboy1,北京)

十二、__module__和__class__

  • __module__ 表示当前操作的对象在那个模块
  • __class__表示当前操作的对象的类是什么
# lib/aa.py
class C:
    def __init__(self):
        self.name = 'SB'

# index.py
from lib.aa import C

obj = C()
print(obj.__module__)  # 输出 lib.aa,即:输出模块
print(obj.__class__)  # 输出 lib.aa.C,即:输出类

十三、实现文件上下文管理(__enter__和__exit__)

with语句,即上下文管理协议,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法。

  • 使用with语句的目的就是把代码块放入with中执行,with结束后,自动完成清理工作,无须手动干预
  • 在需要管理一些资源比如文件,网络连接和锁的编程环境中,可以在__exit__中定制自动释放资源的机制,你无须再去关系这个问题,这将大有用处。

__exit__()中的三个参数分别代表异常类型,异常值和追溯信息。with语句中代码块出现异常,则with后的代码都无法执行。

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

    def __enter__(self):
        print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with中代码块执行完毕时执行我啊')
        print(exc_type)
        print(exc_val)
        print(exc_tb)

try:
    with Open('a.txt') as f:
        print('=====>执行代码块')
        raise AttributeError('***着火啦,救火啊***')
except Exception as e:
    print(e)

# 出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量
# =====>执行代码块
# with中代码块执行完毕时执行我啊
# <class 'AttributeError'>
# ***着火啦,救火啊***
#
# ***着火啦,救火啊***

如果__exit()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行。

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

    def __enter__(self):
        print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')

    def __exit__(self, exc_type, exc_val, exc_tb):
        print('with中代码块执行完毕时执行我啊')
        print(exc_type)
        print(exc_val)
        print(exc_tb)
        return True

with Open('a.txt') as f:
    print('=====>执行代码块')
    raise AttributeError('***着火啦,救火啊***')
print('0' * 100)  # ------------------------------->会执行

# 出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量
# =====>执行代码块
# with中代码块执行完毕时执行我啊
# <class 'AttributeError'>
# ***着火啦,救火啊***
#
# 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

#

模拟open

class Open:
    def __init__(self, filepath, mode='r', encoding='utf-8'):
        self.filepath = filepath
        self.mode = mode
        self.encoding = encoding

    def __enter__(self):
        # print('enter')
        self.f = open(self.filepath, mode=self.mode, encoding=self.encoding)
        return self.f

    def __exit__(self, exc_type, exc_val, exc_tb):
        # print('exit')
        self.f.close()
        return True

    def __getattr__(self, item):
        return getattr(self.f, item)

with Open('a.txt', 'w') as f:
    print(f)
    f.write('aaaaaa')
    f.wasdf  #抛出异常,交给__exit__处理

# <_io.TextIOWrapper name='a.txt' mode='w' encoding='utf-8'>

十四、描述符(__get__和__set__和__delete__)

描述符是可以实现大部分python类特性中的底层魔法,包括@classmethod,@staticmethd,@property甚至是__slots__属性。

描述符是很多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架中的一个组件。

描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议

  • __get__():调用一个属性时,触发
  • __set__():为一个属性赋值时,触发
  • __delete__():采用del删除属性时,触发

描述符的作用是用来代理另外一个类的属性的。包含这三个方法的新式类称为描述符,由这个类产生的实例进行属性的调用/赋值/删除,并不会触发这三个方法

class Foo:
    def __get__(self, instance, owner):
        print('触发get')

    def __set__(self, instance, value):
        print('触发set')

    def __delete__(self, instance):
        print('触发delete')

f1 = Foo()
f1.name = 'nick'
f1.name
del f1.name
#无任何输出结果!!!

必须把描述符定义成这个类的类属性,不能定义到构造函数中。

class ST:
    """描述符Str"""

    def __get__(self, instance, owner):
        print('Str调用')

    def __set__(self, instance, value):
        print('Str设置...')

    def __delete__(self, instance):
        print('Str删除...')

class IN:
    """描述符Int"""

    def __get__(self, instance, owner):
        print('Int调用')

    def __set__(self, instance, value):
        print('Int设置...')

    def __delete__(self, instance):
        print('Int删除...')

class People:
    name = ST()
    age = IN()

    def __init__(self, name, age):  # name被ST类代理,age被IN类代理
        self.name = name
        self.age = age

p1 = People('alex', 18)
# Str设置...
# Int设置...

p1.name  # Str调用
p1.name = 'nick'  # Str设置...
del p1.name  # Str删除...

p1.age  # Int调用
p1.age = 18  # Int设置...
del p1.age  # Int删除...

print(p1.__dict__)  # {}
print(People.__dict__)
# {'__module__': '__main__', 'name': <__main__.ST object at 0x0000000002167490>, 'age': <__main__.IN object at 0x000000000234A700>, '__init__': , '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}

print(type(p1) == People)  # True
print(type(p1).__dict__ == People.__dict__)  # True

1、使用描述符

众所周知,python是弱类型语言,即参数的赋值没有类型限制,下面我们通过描述符机制来实现类型限制功能。

class Typed:
    def __init__(self, name, expected_type):
        self.name = name
        self.expected_type = expected_type

    def __get__(self, instance, owner):
        print('get--->', instance, owner)
if instance is None:
return self
return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print('set--->', instance, value)
       if not isinstance(value, self.expected_type):
raise TypeError('Expected %s' % str(self.expected_type))
        instance.__dict__[self.name] = value

    def __delete__(self, instance):
        print('delete--->', instance)
        instance.__dict__.pop(self.name)

class People:
    name = Typed('name', str)
    age = Typed('name', int)
    salary = Typed('name', float)

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

try:
    p1 = People(123, 18, 3333.3)
except Exception as e:
    print(e)
# set---> <__main__.People object at 0x1082c7908> 123
# Expected <class 'str'>

try:
    p1 = People('nick', '18', 3333.3)
except Exception as e:
    print(e)
# set---> <__main__.People object at 0x1078dd438> nick
# set---> <__main__.People object at 0x1078dd438> 18
# Expected <class 'int'>

p1 = People('nick', 18, 3333.3)
# set---> <__main__.People object at 0x1081b3da0> nick
# set---> <__main__.People object at 0x1081b3da0> 18
# set---> <__main__.People object at 0x1081b3da0> 3333.3

2、类的装饰器:无参

def decorate(cls):
    print('类的装饰器开始运行啦------>')
    return cls

@decorate  # 无参:People = decorate(People)
class People:
    def __init__(self, name, age, salary):
        self.name = name
        self.age = age
        self.salary = salary

p1 = People('nick', 18, 3333.3)

# 类的装饰器开始运行啦------>

3、类的装饰器:有参

def typeassert(**kwargs):
    def decorate(cls):
        print('类的装饰器开始运行啦------>', kwargs)
        return cls

    return decorate

@typeassert( name=str, age=int, salary=float)  # 有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People)
class People:
    def __init__(self, name, age, salary):
        self.name = name
        self.age = age
        self.salary = salary

p1 = People('nick', 18, 3333.3)
# 类的装饰器开始运行啦------> {'name': <class 'str'>, 'age': <class 'int'>, 'salary': <class 'float'>}

4、描述符与类装饰器结合使用

class Typed:
    def __init__(self, name, expected_type):
        self.name = name
        self.expected_type = expected_type

    def __get__(self, instance, owner):
        print('get--->', instance, owner)
        if instance is None:
            return self
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print('set--->', instance, value)
        if not isinstance(value, self.expected_type):
            raise TypeError('Expected %s' % str(self.expected_type))
        instance.__dict__[self.name] = value

    def __delete__(self, instance):
        print('delete--->', instance)
        instance.__dict__.pop(self.name)

def typeassert(**kwargs):
   def decorate(cls):
        print('类的装饰器开始运行啦------>', kwargs)
        for name, expected_type in kwargs.items():
            setattr(cls, name, Typed(name, expected_type))
        return cls

    return decorate

@typeassert(name=str, age=int,  salary=float)  # 有参:1.运行typeassert(...)返回结果是decorate,此时参数都传给kwargs 2.People=decorate(People)
class People:
    def __init__(self, name, age, salary):
        self.name = name
        self.age = age
        self.salary = salary

print(People.__dict__)
# 类的装饰器开始运行啦------> {'name': <class 'str'>, 'age': <class 'int'>, 'salary': <class 'float'>}
# {'__module__': '__main__', '__init__': , '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None, 'name': <__main__.Typed object at 0x000000000238F8B0>, 'age': <__main__.Typed object at 0x000000000238FF40>, 'salary': <__main__.Typed object at 0x000000000238FFA0>}

p1 = People('nick', 18, 3333.3)

# set---> <__main__.People object at 0x0000000001E07490> nick
# set---> <__main__.People object at 0x0000000001E07490> 18
# set---> <__main__.People object at 0x0000000001E07490> 3333.3

5、利用描述符原理自定制@property

实现延迟计算(本质就是把一个函数属性利用装饰器原理做成一个描述符:类的属性字典中函数名为key,value为描述符类产生的对象)

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

    def __get__(self, instance, owner):
        print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')
        if instance is None:
            return self
        else:
            print('--->')
            value = self.func(instance)
            setattr(instance, self.func.__name__, value)  # 计算一次就缓存到实例的属性字典中
            return value

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

    @Lazyproperty  # area=Lazyproperty(area) 相当于'定义了一个类属性,即描述符'
    def area(self):
        return self.width * self.length

r1 = Room('alex', 1, 2)
print(r1.area)  # 先从自己的属性字典找,没有再去类的中找,然后出发了area的__get__方法

# 这是我们自己定制的静态属性,r1.area实际是要执行r1.area()
# --->
# 2

print(r1.area)  # 先从自己的属性字典找,找到了,是上次计算的结果,这样就不用每执行一次都去计算
# 2

6、自定制@classmethod

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

    def __get__(self, instance, owner):  # 类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身,
        def feedback(*args, **kwargs):
            print('在这里可以加功能啊...')
            return self.func(owner, *args, **kwargs)

        return feedback

class People:
    name = 'nick'

    @ClassMethod  # say_hi=ClassMethod(say_hi)
    def say_hi(cls, msg):
        print('你好啊,帅哥 %s %s' % (cls.name, msg))

People.say_hi('你是那偷心的贼')

p1 = People()
# 在这里可以加功能啊...
# 你好啊,帅哥 nick 你是那偷心的贼

p1.say_hi('你是那偷心的贼')
# 在这里可以加功能啊...
# 你好啊,帅哥 nick 你是那偷心的贼

7、自定制@staticmethod

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

    def __get__(self, instance, owner):  # 类来调用,instance为None,owner为类本身,实例来调用,instance为实例,owner为类本身
        def feedback(*args, **kwargs):
            print('在这里可以加功能啊...')
            return self.func(*args, **kwargs)

        return feedback

class People:
    @StaticMethod  # say_hi = StaticMethod(say_hi)
    def say_hi(x, y, z):
        print('------>', x, y, z)

People.say_hi(1, 2, 3)
# 在这里可以加功能啊...
# ------> 1 2 3

p1 = People()
p1.say_hi(4, 5, 6)
# 在这里可以加功能啊...
# ------> 4 5 6

十五、元类(metaclass)

元类:负责产生该对象的类称之为元类,即元类可以简称为类的类

用class关键字创建一个类,用的默认的元类type,因此以前说不要用type作为类别判断

class People:  # People=type(...)
    country = 'China'

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

    def eat(self):
        print('%s is eating' % self.name)

print(type(People))
<class 'type'>

1、type实现

  • 创建类的3个要素:类名,基类,类的名称空间
  • People = type(类名,基类,类的名称空间)
class_name = 'People'  # 类名
class_bases = (object,)  # 基类

class_dic = {}  # 类的名称空间
class_body = """
country='China'
def __init__(self,name,age):
    self.name=name
    self.age=age
def eat(self):
    print('%s is eating' %self.name)
"""

exec(class_body, {}, class_dic, )  #执行class_body中的代码,然后把产生的名字丢入class_dic字典中

print(class_name)  # People
print(class_bases)  # (<class 'object'>,)
print(class_dic)  # 类的名称空间
# {'country': 'China', '__init__': , 'eat': }

People1 = type(class_name, class_bases, class_dic)
print(People1)  # <class '__main__.People'>

obj1 = People1(1, 2)
obj1.eat()  # 1 is eating

2、自定义元类控制类

自定义元类控制类的产生过程,类的产生过程其实就是元类的调用过程。

分析用class自定义类的运行原理(而非元类的的运行原理):

  • 拿到一个字符串格式的类名class_name='People'
  • 拿到一个类的基类们class_bases=(obejct,)
  • 执行类体代码,拿到一个类的名称空间class_dic={...}
  • 调用People=type(class_name,class_bases,class_dic)
class Mymeta(type):  # 只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
    def __init__(self, class_name, class_bases, class_dic):
        print('self:', self)  # 现在是People
        print('class_name:', class_name)
        print('class_bases:', class_bases)
        print('class_dic:', class_dic)
        super(Mymeta, self).__init__(class_name, class_bases, class_dic)  # 重用父类type的功能

class People(object, metaclass=Mymeta):  # People=Mymeta(类名,基类们,类的名称空间)
    country = 'China'

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

    def eat(self):
        print('%s is eating' % self.name)

# self: <class '__main__.People'>
# class_name: People
# class_bases: (<class 'object'>,)
# class_dic: {'__module__': '__main__', '__qualname__': 'People', 'country': 'China', '__init__': , 'eat': }

应用:我们可以控制类必须有文档。

class Mymeta(type):  # 只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
    def __init__(self, class_name, class_bases, class_dic):
        if class_dic.get('__doc__') is None or len(
                class_dic.get('__doc__').strip()) == 0:
            raise TypeError('类中必须有文档注释,并且文档注释不能为空')
        if not class_name.istitle():
            raise TypeError('类名首字母必须大写')
        super(Mymeta, self).__init__(class_name, class_bases,   class_dic)  # 重用父类的功能

try:

    class People(object, metaclass=Mymeta ):  # People  = Mymeta('People',(object,),{....})
        #     """这是People类"""
        country = 'China'

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

        def eat(self):
            print('%s is eating' % self.name)
except Exception as e:
    print(e)
# 类中必须有文档注释,并且文档注释不能为空

3、自定义元类控制类的实例化

类的调用,即类实例化就是元类的调用过程,可以通过元类Mymeta的__call__方法控制。

继承的查找顺序:子类->Class –>object–> Mymeta->type

class Mymeta(type):
    def __call__(self, *args, **kwargs):
        print(self)  # self是People
        print(args)  # args = ('nick',)
        print(kwargs)  # kwargs = {'age':18}
        # return 123
        # 1. 先造出一个People的空对象,申请内存空间
        # __new__方法接受的参数虽然也是和__init__一样,但__init__是在类实例创建之后调用,而 __new__方法正是创建这个类实例的方法。
        obj = self.__new__(self)  # 虽然和下面同样是People,但是People没有,找到的__new__是父类的
        # 2. 为该对空对象初始化独有的属性
        self.__init__(obj, *args, **kwargs)
        # 3. 返回一个初始化好的对象
        return obj

class People(object, metaclass=Mymeta):  # People = Mymeta(),People()则会触发__call__
    country = 'China'

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

    def eat(self):
        print('%s is eating' % self.name)

#     在调用Mymeta的__call__的时候,首先会找自己(如下函数)的,自己的没有才会找父类的
#     def __new__(cls, *args, **kwargs):
#         # print(cls)  # cls是People
#         # cls.__new__(cls) # 错误,无限死循环,自己找自己的,会无限递归
#         obj = super(People, cls).__new__(cls)  # 使用父类的,则是去父类中找__new__
#         return obj

obj = People('nick', age=18)
# <class '__main__.People'>
# ('nick',)
# {'age': 18}

print(obj.__dict__)
# {'name': 'nick', 'age': 18}

4、练习:使用元类修改属性为隐藏属性

class Mymeta(type):
    def __init__(self, class_name, class_bases, class_dic):
        # 加上逻辑,控制类Foo的创建
        super(Mymeta, self).__init__(class_name, class_bases, class_dic)

    def __call__(self, *args, **kwargs):
        # 加上逻辑,控制Foo的调用过程,即Foo对象的产生过程
        obj = self.__new__(self)
        self.__init__(obj, *args, **kwargs)
        # 修改属性为隐藏属性
        obj.__dict__ = {
            '_%s__%s' % (self.__name__, k): v
            for k, v in obj.__dict__.items()
        }

        return obj

class Foo(object, metaclass=Mymeta):  # Foo = Mymeta(...)
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex

obj = Foo('nick', 18, 'male')
print(obj.age)  # 'Foo' object has no attribute 'age'
print(obj.__dict__)
# {'_Foo__name': 'egon', '_Foo__age': 18, '_Foo__sex': 'male'}

5、利用元类实现单例模式

NAME = 'nick'
AGE = 18

class Mymeta(type):
    def __init__(self,class_name,class_bases,class_dict):
        super().__init__(class_name,class_bases,class_dict)
        self.__instance = self(NAME,AGE)

    def __call__(self,*args,**kwargs):

        if len(args) == 0 and len(kwargs) == 0:
return self.__instance

        obj = object.__new__(self)
        self.__init__(obj,*args,**kwargs)

        return obj

class People(metaclass=Mymeta):
    def __init__(self,name,age):
        self.name = name
        self.age = age

peo1 = People()
peo2 = People()

到此这篇关于Python面向对象编程的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Python面向对象编程(一)

    目录 一.程序中定义类和对象 1. 定义类 2. 定义对象 二.定制对象独有特征 1.引入 2.定制对象独有特征 3.对象属性查找顺序 4.类定义阶段定制属性 三.对象的绑定方法 1.类使用对象的绑定对象 2.对象使用对象的绑定方法 四.类与数据类型 Python面向对象编程(一) Python面向对象编程(二) Python面向对象编程(三) 和其它编程语言相比,Python 在尽可能不增加新的语法和语义的情况下加入了类机制. Python中的类提供了面向对象编程的所有基本功能:类的继承机制允

  • Python面向对象编程(二)

    目录 一.对象的继承 1.类的构造函数继承__init__(): 2.继承关系中,对象查找属性的顺序 二.类的派生 1.派生方法一(类调用) 2.派生方法二(super) 三.类的组合 四.多父类继承问题 1.新式类(MRO)列表 2.super()方法详解 五.抽象类 六.类的封装 1.私有属性:双下划线的方式__x 2.外部使用变形访问:_类名__x 3.在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的 七.类的属性(property) 1.装饰器方式 (推荐使用) 2.经

  • Python 面向对象编程的三大特性之继承

    目录 Python  面向对象编程的三大特性之继承 一.继承 1.继承的实际栗子 2.继承的好处 3.继承的使用场景 4.继承有几种? 5.Python 中继承的简单语法 二.不使用继承.使用继承的区别 1.需求背景 2.不使用继承 2.使用继承 三.继承的传递性 1.什么是传递性 四.继承和抽象 1.继承的重点 Python  面向对象编程的三大特性之继承 一.继承 继承也是面向对象编程三大特性之一 继承是类与类的一种关系 定义一个新的 class 时,可以从某个现有的 class 继承 新的

  • 浅析Python面向对象编程

    概述 很多人接触Python,都是从爬虫开始,其实很多语言都可以做爬虫,只是Python相对其他语言来说,更加简单而已.但是Python并不止于爬虫,在人工智能,科学计算等方面的应用更加广泛.古人云:万丈高楼平地起,要想有长足的发展,打好基础很重要,本文主要讲解Python的面向对象相关知识,仅供学习分享使用,如有不足之处,还请指正. 面向对象的特征 类:用来描述相同事物的特征的集合,如:Person 类,表示人,具有人的属性和特征. 对象:通过类定义的具体的实例,如:zhangsan 表示一个

  • Python学习之面向对象编程详解

    目录 什么是面向对象编程(类) 类的关键字-class 类的定义与使用 类的参数-self self的解析与总结 类的构造函数 构造函数的创建方法 关于对象的生命周期 什么是面向对象编程(类) 利用(面向)对象的(属性和方法)去进行编码的过程即面向对象编程 自定义对象数据类型就是面向对象中的类(class)的概念 类的关键字 - class class 关键字用来声明类,类的名称首字母大写,多单词的情况下每个单词首字母大写(即驼峰命名法).在我们一开始学习 Python 的时候说过,要尽量避免使

  • Python面向对象编程(三)

    目录 一.isinstance和issubclass 二.反射(hasattr和getattr和setattr和delattr) 1.反射在类中的使用 2.反射在模块中的使用 3.实例:基于反射机制模拟web框架路由 三.__getattr__.__setattr__和__delattr__和__getattribute__事件 __getattribute__ 四.__setitem__和__getitem和__delitem__ 五.__format__:自定制格式化字符串 六.__del_

  • Python面向对象编程基础解析(一)

    1.什么是面向对象 面向对象(oop)是一种抽象的方法来理解这个世界,世间万物都可以抽象成一个对象,一切事物都是由对象构成的.应用在编程中,是一种开发程序的方法,它将对象作为程序的基本单元. 2.面向对象与面向过程的区别 我们之前已经介绍过面向过程了,面向过程的核心在'过程'二字,过程就是解决问题的步骤,面向过程的方法设计程序就像是在设计一条流水线,是一种机械式的思维方式 优点:复杂的问题简单化,流程化 缺点:扩展性差 主要应用场景有:Linux内核,git,以及http服务 面向对象的程序设计

  • Python面向对象编程之类的继承

    目录 1.对继承的理解 2.类继承的构建 3.Python中最基础的类 4.ython类的重载 4.1 属性重载 4.2 方法重载 5.类的多继承 1.对继承的理解 继承(Inheritance) :代码复用的高级抽象 继承是面向对象设计的精髓之一 实现了以类为单位的高级抽象级别代码复用 继承是新定义类能够几乎完全使用原有类属性与方法的过程 不管是基类还是派生类,只是一种继承说法,这都是普通的Python类 也可以按子类.父类和超类划分. 最基础的类是基类,经过一次继承得出派生类,还可以再一次继

  • Python面向对象编程基础解析(二)

    Python最近挺火呀,比鹿晗薛之谦还要火,当然是在程序员之间.下面我们看看有关Python的相关内容. 上一篇文章我们已经介绍了部分Python面向对象编程基础的知识,大家可以参阅:Python面向对象编程基础解析(一),接下来,我们看看另一篇. 封装 1.为什么要封装? 封装就是要把数据属性和方法的具体实现细节隐藏起来,只提供一个接口.封装可以不用关心对象是如何构建的,其实在面向对象中,封装其实是最考验水平的 2.封装包括数据的封装和函数的封装,数据的封装是为了保护隐私,函数的封装是为了隔离

  • Python面向对象编程中的类和对象学习教程

    Python中一切都是对象.类提供了创建新类型对象的机制.这篇教程中,我们不谈类和面向对象的基本知识,而专注在更好地理解Python面向对象编程上.假设我们使用新风格的python类,它们继承自object父类. 定义类 class 语句可以定义一系列的属性.变量.方法,他们被该类的实例对象所共享.下面给出一个简单类定义: class Account(object): num_accounts = 0 def __init__(self, name, balance): self.name =

  • 关于Python面向对象编程的知识点总结

    前言 如果你以前没有接触过面向对象的编程语言,那你可能需要先了解一些面向对象语言的一些基本特征,在头脑里头形成一个基本的面向对象的概念,这样有助于你更容易的学习Python的面向对象编程. 接下来我们就来了解关于Python面向对象编程的知识点吧. 类与实例 类是对象的定义,而实例是"真正的实物",它存放了类中所定义的对象的具体信息. 类.属性和方法命名规范 类名通常由大写字母打头.这是标准惯例,可以帮助你识别类,特别是在实例化过程中(有时看起来像函数调用).还有,数据属性(变量或常量

  • 详解Python:面向对象编程

    面向过程的程序设计把计算机程序视为一系列的命令集合,即一组函数的顺序执行.为了简化程序设计,面向过程把函数继续切分为子函数,即把大块函数通过切割成小块函数来降低系统的复杂度 python和java都是面向对象的语言.面向对象编程的特点数据封装.继承和多态 1.类和实例 面向对象最重要的概念就是类(Class)和实例(Instance),必须牢记类是抽象的模板,比如Student类,而实例是根据类创建出来的一个个具体的"对象",每个对象都拥有相同的方法,但各自的数据可能不同. 在pyth

随机推荐