Reflection & command distributor

  

Runtime, different from compile time, refers to the time when the program is loaded into memory for execution

reflection refers to getting the information of type definition at runtime

An object can reflect its type information at runtime, just like looking for a mirror

In Python, the ability to find out the type, class, attribute and method of an object is called reflection or introspection

Reflection function: type() isinstance() callable() dir() getattr()

def dispatcher():
    cmds={}
    def reg(cmd,fn):
        if isinstance(cmd,(str,)):
            cmds.setdefault(cmd,fn)
        else:
            raise TypeError('TypeError')
        print(cmds)

    def run():
        while True:
            cmd=input('cmd: ').strip()
            if cmd.lower() == 'q':
                return
            print(cmds.get(cmd,default)())

    def default():
        print('default')
    return reg,run

reg,run=dispatcher()
reg('b1',lambda :print(11111111))
reg('b2',lambda :222)
print(run())
class Dispatcher:
    def cmd1(self):
        return 'cmd1'
    def reg(self,cmd,fn):
        if isinstance(cmd,(str,)):
            # setattr(self.__class__,cmd,fn)
            setattr(type(self),cmd,fn)
        else:
            raise TypeError

    def run(self):
        while True:
            cmd=input('commmand: ').strip()
            if cmd.upper() == 'Q':
                return
            print(getattr(self,cmd,self.default)())

    def default(self):
        return 'default'
b=Dispatcher()
b.reg('cmd2',lambda self:22222222)
b.reg('cmd3',lambda self:333333333333)
b.run()
class Dispatcher:
    def __init__(self):
        print('aaaaaaaaaa')
        self._run() # The instance is called upon initialization_ run method (of class)
        print(self.__dict__)
        print('bbbbbbbbbbbbbbbb')
        # self.b
    def cmd1(self):
        return 'cmd1'
    def cmd2(self):
        return 'cmd2'

    def _run(self):
        while True:
            cmd=input('cmd: ').strip()
            if cmd.lower() == 'q':
                return
            print(getattr(self,cmd,lambda : 'default')())

Dispatcher()
# print(b.__dict__)

 

Reflection related functions and methods

A Point class, view its instance properties, and dynamically increase its properties

class Point:
    def __init__(self,x,y):
        self.x=x
        self.y=y

    def __str__(self):
        return 'Point:{{{}:{}}}'.format(self.x,self.y)
    def show(self):
        print('show:{{{}:{}}}'.format(self.x,self.y))

p=Point(22,33)
print(str(p))
print(p.__dict__)
p.__dict__['y']=44
print(p.__dict__)
p.z=10
print(p.__dict__)
print(dir(p),type(dir(p)))
print(p.__dir__())

In the above example, the access method is not elegant, and Python provides built-in functions

Built in function significance
getattr(object,name[,default]) Access the object attribute through name. If the attribute does not exist, default is returned. If default is not set, attributeerror is thrown. Name must be a string
setattr(object,name,value) object attribute exists, overwrites, does not exist, adds
hasattr(object,name) Judge whether the object has the attribute of name. Name must be str

 

Rewrite the code in the above way

class Point:
    def __init__(self,x,y):
        self.x=x
        self.y=y

    def __str__(self):
        return 'Point:{{{}:{}}}'.format(self.x,self.y)
    def show(self):
        print(self)

p1=Point(11,22)
p2=Point(33,44)
print(repr(p1),repr(p2),sep='\n')
print(p1.__dict__)
setattr(p1,'y',999)
setattr(p1,'z',888)
print(getattr(p1,'__dict__'))
print(p1.__dict__)
# Dynamic call method
if hasattr(p1,'show'):
    p1.show()
if hasattr(p1,'uiop'):
    p1.uiop()
#Dynamic increase method
# Adding methods to classes
if not hasattr(Point,'add'):
    setattr(Point,'add',lambda self,other:Point(self.x+other.x,self.y+other.y))

print(Point.add)
print(p1.add)
print(p1.add(p2))
# Add method for instance, unbound
if not hasattr(p1,'sub'):
    setattr(p1,'sub',lambda self,other:Point(self.x-other.x,self.y-other.y))
print(p1.sub(p1,p1))
print(p1.sub) # self parameter not required
print(p1)
def fn(self,other):
    self.x=self.x-other.x
    self.y=self.y-other.y
    return self

if hasattr(p1,'sub'):
    setattr(p1,'sub',lambda self,other:exec('self.x=self.x-other.x;self.y=self.y-other.y'))
    # setattr(p1,'sub',fn)

print(p1.sub)
print(p1.sub(p1,p1))
print(p1)

print(p1.__dict__)
print(Point.__dict__)

 

 

 

Reflection related magic method

__getattr__       __setattr__         __delattr__

__getattr__:

class Base:
n=00
class Point(Base):
z=6
def __init__(self,x,y):
self.x=x
self.y=y
def show(self):
print(self.x,self.y)
# def __getattr__(self,item):
# return '{} object missing {}'.format(self.__class__.__name__,item)

p1=Point(11,22)
print(p1.x)
print(p1.z)
print(p1.n)
# print(p1.mm)
print(getattr(p1,'uio','uioopp'))
# use getattr()Time,If class exists__getattr__Magic Methods ,Optional default,If not set default,Nor does it exist__getattr__An exception will be thrown
# When accessing properties directly, there is no__ getattr__ Direct throw anomaly

The attribute will be found according to the inheritance relationship. If it cannot be found, it will be executed__ getattr__ Method. If there is no such method, an AttributeError exception will be thrown, indicating that the attribute cannot be found

The order of finding attributes is:
instance.__dict__        =>           instance.__class__.__dict__ = > inherited ancestor class (until object)__ dict__ = > call getattr()

 

__setattr__

 

class Base:
    n=00
class Point(Base):
    z=6
    def __init__(self,x,y):
        self.x=x
        self.y=y

    def show(self):
        print(self.x,self.y)

    def __getattr__(self,item):
        return '{} object missing {}'.format(self.__class__.__name__,item)

    def __setattr__(self,key,value):
        print('setattr {}={}'.format(key,value)) # After overwriting the method of object, the attribute is not written to dict

p1=Point(11,22)
print(p1.x)
print(p1.z)
print(p1.n)
print(p1.mm)
p1.x=55
print(p1.__dict__)
p1.__dict__['x']=66 # Directly manipulate the properties of the instance itself__ dic__ Not called__ setattr__
print(p1.__dict__)
print(p1.x)
setattr(p1,'vv',888)
print(p1.__dict__)

Instance passed "." Or setattr to set the attribute, like self X ='xxxxxxx 'or' setattr(p1,'x','xxxxxxxxxxxx'), it will call__ setattr__ () magic method, but directly operate your own dictionary, like P1__ dict__ ['x']='xxxxxxxxxxxxx'

Will not be triggered__ setattr__ Implementation of

 

class Point(Base):
    z=6
    def __init__(self,x,y):
        self.x=x
        self.y=y

    def show(self):
        print(self.x,self.y)

    def __getattr__(self,item):
        return '{} object missing {}'.format(self.__class__.__name__,item)

    def __setattr__(self,key,value):
        print('setattr {}={}'.format(key,value)) # After overwriting the method of object, the attribute is not written to dict
        self.__dict__[key]=value # Perform write operation

 

__ setattr__ Method, you can intercept the addition and modification of instance properties. If you want the settings to take effect, you need to manually operate the instance properties__ dict__

 

 

__delattr__

 

class Ore:
    z=55
    def __init__(self,x,y):
        self.x=x
        self.y=y

    def __delattr__(self,item): # Example method
        print('Can not del {}'.format(item))

p=Ore(11,22)
del p.x
p.z=33
del p.z
print(Ore.__dict__)
print(p.__dict__)
del Ore.z
print(Ore.__dict__)
print(p.__dict__)

You can prevent the operation of deleting attributes through instances, but you can still delete attributes through classes, because these three magic methods are aimed at instances

 

__getattribute__

 

class Base:n=00
class Ore(Base):
    z=66
    def __init__(self,x,y):
        self.x=x
        self.y=y

    def __getattr__(self,item):
        return '{} object missing {}'.format(self.__class__.__name__,item)

    def __getattribute__(self,item):
        return item

o=Ore(11,22)
print(o.__dict__)
print(o.x)
print(o.z)
print(o.mmmm)
print(Ore.__dict__)
print(Ore.z)

When accessing all the properties of an instance, the first one will be called__ getattribute__ Method__ getattribute__ Prevents the search of instance attributes. This method returns the calculated value or throws an AttributeError exception

Its return value will be the result of attribute search. If an AttributeError exception is thrown, it will be called directly__ getattr__ Method, indicating that the property was not found

class Base:n=00
class Ore(Base):
    z=66
    def __init__(self,x,y):
        self.x=x
        self.y=y

    def __getattr__(self,item):
        return '{} object missing {}'.format(self.__class__.__name__,item)

    def __getattribute__(self,item):
        raise AttributeError('Attribute Not Found')
        return item

o=Ore(11,22)
print(o.__dict__)

 

The figure above generates a circular call

Modify code

class Base:n=00
class Ore(Base):
    z=66
    def __init__(self,x,y):
        self.x=x
        self.y=y

    def __getattr__(self,item):
        return '{} object missing {}'.format(self,item)

    def __getattribute__(self,item):
        raise AttributeError('Attribute Not Found')
        return item

o=Ore(11,22)
print(o.__dict__)
print(o.x)
print(o.z)
print(o.mmmm)
print(Ore.__dict__)
print(Ore.z)
 

 

To access all instance properties, call__ getattribute__ Directly raise AttributeError and then call__ getattr__ method

class Base:n=00
class Ore(Base):
    z=66
    def __init__(self,x,y):
        self.x=x
        self.y=y

    def __getattr__(self,item):
        return '{} object missing {}'.format(self,item)

    def __getattribute__(self,item):
        # raise AttributeError('Attribute Not Found')
        # return self.__dict__[item] # Circular recursion will occur
        return object.__getattribute__(self,item) # Correct return method

o=Ore(11,22)
print(o.__dict__)
print(o.x)
print(o.z)
print(o.mmmm)
print(Ore.__dict__)
print(Ore.z)

 

__ getattribute__ Method, in order to avoid infinite recursion in this method, its implementation should always call the method with the same name of the base class to access any required properties, such as object getattribute(self,name)

Magic Methods significance
__getattr__ Called when the attribute cannot be found by searching the instance, the class of the instance, and the ancestor class
__setattr__ By "." Or setattr(object,key,value) to access the instance attribute, add it, and call it when modifying
__delattr__ Called when a property is deleted through an instance
__getattribute__ All property calls of the instance start from this method

 

Attribute lookup order:

__ getattribute__ ()    =>       instance.__dict__           =>            instance.__class__.__dict__ = > until object__ dict__                  =>           __ getattr__ ()

 

Posted by johnseito on Thu, 12 May 2022 21:58:49 +0300