14.继承与授权

反射

自省:程序能够访问,检测和修改他本身的状态或者行为的能力

通过字符串的形式操作对象相关属性。

class people:
    country = 'China'
    def __init__(self,name):
        self.name = name

p = people('scott')
people.country
print(people.__dict__)

存在:hasatter(p,'name')

p这个对象下,有没有‘name’这个属性
其实就是
print('name' in p.__dict__)
返回一个bool值

获得:getattr

p这个对象下,调用country参数
gatattr(p,'country')
等同于
p.country

def getattr(object, name, default=None): # known special case of getattr
    """
    getattr(object, name[, default]) -> value

    Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y.
    When a default argument is given, it is returned when the attribute doesn't
    exist; without it, an exception is raised in that case.
    """
    pass

设置:setattr

setattr(p,'country','China')
等同于
p.country = 'China'

提问:

>>> l = [1,2,4,5,8]
>>> setattr(l,'append','China')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object attribute 'append' is read-only

小结练习:

class People:#定义一个类
    def __init__(self,name):
        self.name = name#提供一个绑定参数

p1 =People('scott')#实例化p1
p2 =People('jerry')#实例化p2
#给p1对象添加一个‘country’属性,值为‘China’
setattr(p1,'country','China')
#使用getattr获取这个属性并且打印
print(getattr(p1,'country'))
#以上那个country参数,是我添加给对象p1的,查看p2是否也获得了这个参数?-----并没有,这个参数单独被加在p1这个对象中
print(hasattr(p2,'country'))
# 使用delattr删除刚才添加的country参数
delattr(p1,'country')
#检查
hasattr(p1,'country')
#定义一个函数func
def func(self):
    print(self.name,' like python')
#利用lambda给p1对象传入一个work方法
setattr(p1,'work',lambda self: (self.name,'like python') )
#给p1传入func方法
setattr(p1,'work2',func )
#目前两个work方法均是针对p1传值的,但是在下方实例化p1的work2方法为h,并且对两个对象进行work2方法,发现两个对象均可以使用
#设置setattr可以将一个函数或者参数指向添加给某个对象的类
#虽然在设置中,设置的P1但是其实和p1同类的实例均可以调用

print(hasattr(p1,'work'))

g = getattr(p1,'work')
h = getattr(p1,'work2')
print(g(p1))
h(p1)
h(p2)

反射的用途

反射当前模块的属性

import sys
#导入系统模块
x = 1111
class foo:
    pass
def s1():
    print('s1')
def s2():
    print('s2')
this_module = sys.modyles[__name__]
#将当前写的程序作为一个模块“this_module”
def add():
    print('add')

def change():
    print('change')

def search():
    print('search')

def delete():
    print('delete')

func_dic = {
    'add':add
    'change':change
    'search':search
    'delete':delete
}
  • 方法一(常规)
while True:
    cmd = input('>>:')
    if not cmd:continu
    if cmd in func_dic:        #hasattr()
        func= func_dic.get(cmd)    #gatattr()
        func()
  • 方法二(利用反射实现)
this_module = sys.modyles[__name__]
#将以上程序归为一个模块,命名为this_module
while True:
    cmd = input('>>>').strip()
    if not cmd:continu
    if hasatter(this_module,cmd):
        func = getattr(this_module,cmd)
        func()

反射实现可插拔机制

  • server
from ftp_1 import FtpClient
#从FIP_1中载入FtpClient模块
f1 = FtpClient('192.168.1.1')
#实例化
if hasattr(f1,'get'):
#查看能不能从对象中获取一个‘get’函数
    func_get = getattr(f1,'get')
    func_get()#如果能获取就运行
else:
#如果没有就打出标志位
    print('---------->')

在上述程序中,如果在FtpClient文件中没有定义一个get函数。并不会影响上述程序执行。但是如果FtpClient文件补全了了这个get函数,上述文件并不需要修改,就能正常实现功能

  • ftp module
#提供一个FtpClient类
class FtpClient:
    def __init__(self,addr):
        print('lianjie [%s]'%addr)
        self.addr = addr

通过字符串导入模块

m = input('module:')
m1 = __import__(m)
print(m1)
print(m1.time())

推荐使用以下方法

import importlib
t = importlib.import_module('time')
print(t.time())

__setattr/delattr/getattr__

下面这段代码,串讲一些之前提到的setattr、delattr、getattr,并且介绍__setattr/delattr/getattr__

授权:授权是包装的一个特性,包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能。其他的保持原样。授权的过程,即使所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性

实现授权的关键点就是覆盖__getattr__方法

class Foo:
    def __init__(self,name):
        self.name = name
    def __setattr__(self,key,value):
        print('----setattr----'key,value)
    #由于传入的key和value都是字符串,不能直接跟在self后面
    #self.key = value
    #setattr(self,key,value)#同上
    #在当前函数中设置这个赋值(因为重写了__setattr__,所有的赋值操作都会指向这个函数运行),就相当于把这个函数变成一个无限递归。
    #正确的方法应该是,直接操作__dict__
    self.__dict__[key] = value

    def __delattr__(self,item):
        print('delattr:%s'%item)
        print(type(item))
        # delattr(self,item)
        # del self.item
        self.__dict__.pop(item)
        #原理同上,为了防止递归和字符串类型干扰,应该直接操作__dict__

    def __getattr__(self,item);
        print('get------>%s  %s'%(item,type(item)))

f = Foo('scott')
print(f.name)
#当一个属性存在的时候,正常寻找self.name
print(f.xxxxx)
#当属性不存在的时候,才触发__geattr__


f1 = Foo('egon')
f1.age = 18

print(f1.__dict__)

字典为空,之前的两次传入调用参数,都直接进入了__setattr__

定制自己的数据类型

写一个类,继承原来的list类

class List(list):
    def append(self,p_object):
        if not isinstance(p_object,int):
            raise TypeError('str')
        super().append(p_object)

写一个自己的open

由于open是一个函数,所以不能直接继承

import time

class Open:
    def __init__(self,filepath,m = 'r',encode = 'utf8'):
        self.x= open(filepath,mode = m,encoding = 'utf8')
        #以上self.x就是一个文件句柄,之后需要原文件操作类赋予的动作均使用这个句柄来进行。
        self.mode = m
        self.encoding = encode

    def write(self,line):
        print('time',line)
        self.x.write(time.strftime('%Y-%m-%d %X'),line)

    #授权得分
    def __getattr__(self,item):
        print('--->',item,type(item))
        getattr(self.x,item)
    #在当前类找不到某个方法的时候,因为self.x是一个可以调用各种文件操作的句柄,所以使用getattr方法,来获得这些方法,并且在上方提示。
    #有了这个函数定义之后,就可以在f中使用各个文件操作方式。

f =Open('b.txt','w+')
print(f)
f.write('111111111111')
print(f.read())
f.seek(0)

二次加工标准类型list

要求使用授权的方式:

'''
作业:
    基于授权定制自己的列表类型,要求定制的自己的__init__方法,
    定制自己的append:只能向列表加入字符串类型的值
    定制显示列表中间那个值的属性(提示:property)
    其余方法都使用list默认的(提示:__getattr__加反射)
'''
class List:
    def __init__(self,LOBJECT):
        #将self.x定义为list类包装过的传入参数LOBJECT,以此来获得list的一些方法
        self.x = list(LOBJECT)

    #重新定义append方法,使所有添加进来的参数均转换成str数据类型
    def append(self,value):
        print('后添加的元素都是str格式!')
        if not isinstance(value,str):
            self.x.append(str(value))
            # raise TypeError('ERROR')
        else:
            self.x.append(value)

    #特征化一个函数,这个函数会返回一个列表的中间值,如果这个列表的元素数量为偶数,就返回中间两个值。
    @property
    def mid_list(self):
        len_th =len(self.x)
        if len_th%2==0:
            f1 = self.x[(len_th//2)]
            f2 = self.x[(len_th//2)-1]
            return f1,f2
        else:
            return self.x[len_th // 2]
    #授权self.x获得其他没有定义的关于list的函数,因为传入的对象为self.x,它属于list类。
    def __getattr__(self, item):
        getattr(self.x,item)
    #为了让List对象在打印的时候也和list对象一样,一旦打印就能获得将传入数据类型变为list的能力,在这里使用__str__,使得每次传入的参数返回出来都是list
    def __str__(self):
        return str(self.x)

y = (4,5,7,8,6)

k = List((1,2,4,5,7))
l = List([1,2,3,4,5,6])
# print(l)
print(k)
print(List(y))
# l.append(12)
print(k.mid_list)

补充一点__getattribute__

#_*_coding:utf-8_*_
__author__ = 'Linhaifeng'

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

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

<wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">

原文地址:https://www.cnblogs.com/scott-lv/p/7468956.html