1 What is reflection? And application scenarios?

test.py

def f1():
   print('f1')
def f2():
   print('f2')
def f3():
   print('f3')
def f4():
   print('f4')
a = 1
Copy the code
import test as ss
ss.f1()
ss.f2()
print(ss.a)
Copy the code

If we want to import another module, we can use import. If we need to dynamically enter a module name, we can access the methods or variables in the imported module at any time.

Imp = input(" please input the name of the module you want to import: ") CC = __import__(imp)# execute the f1 method in the module

Or use the importlib module
import importlib
importlib.import_module(name="",package="")
Copy the code

Above we implemented dynamic input of module name, which allows us to enter module name and execute function inside. There is a drawback, however, that the functions executed are fixed. So, can we improve, dynamically enter the function name, and execute it?

#dynamic.py
imp = input("Please enter the module :")
dd = __import__(imp)
# equivalent to import IMP
inp_func = input("Please enter the function to execute:") f = getattr(dd,inp_func, None)Find the function you need to call inp_func from the import module and return a reference to that function. None is annoying if you don't find it
f() # execute this function
Copy the code

So much for the reflection mechanism, what is the reflection mechanism?

In fact, reflection is importing a module as a string; Find the specified function in the module as a string and execute it. Use string form to object (module) operation (find/get/delete/add) members, a string based event driven!

  1. The getattr() function is Python’s core introspection function, and is used as follows:
class A: 
def __init__(self): 
  self.name = 'zhangjing'
  #self.age='24'
def method(self): 
  print"method print"
Instance = A() 
print getattr(Instance , 'name, 'not find') # Print self.name if Instance has property name, otherwise print 'not find'
print getattr(Instance , 'age', 'not find') # print self.age if Instance has property age, otherwise print 'not find'
print getattr(a, 'method', 'defaultDefault print getattr(a, ') # print getattr(a, ')method', 'default')() # Run the function if there is a method and print None otherwise print defaultCopy the code
  1. hasattr(object, name)
(Hasattr is implemented by calling getattr(ojbect, name) to see if an exception is thrown)Copy the code
  1. setattr(object, name, value)
This corresponds to getattr(). Arguments are an object, a string, and an arbitrary value. The string may list an existing attribute or a new attribute. This function assigns the value to the property's. This object allows it to provide. For example,setattr(x, "foobar",123) is equivalent to x.foobar = 123.Copy the code
  1. delattr(object, name)
A set of functions related to setattr(). Arguments are made up of an object (remember in Python everything is an object) and a string. The string argument must be one of the object property names. This function removes a string-specified property of the obj. delattr(x,'foobar'R = hasattr(Commons, XXX) to determine whether a function or variable existsprint(r)  
setattr(commons,'age'Add a global variable age = 18 to Commons module, return none setattr(config,'age'Lambda a:a+1) // Add a function to the module delattr(Commons,'age') / / delete module in a variable or function note: getattr, hasattr, setattr, delattr modifications are in memory of module, does not affect file content.Copy the code

2 What does metaclass do? And application scenarios?

The First Python classes discussed here are based on new classes that inherit from Object.

First of all, in Python, everything is an object. This is a very important statement to understand metaclasses and I’m going to rethink classes in Python

class Trick(object):
   pass
Copy the code

When Python executes a class statement, it initializes a class object and places it in memory. Here, for example, a Trick object is initialized

This object (class) has its own ability to create objects (often called instances, but still objects in Python).

For the sake of further understanding, we can first try the oldest and most powerful keyword in the new class, type.

input:
class Trick(object):
   pass
print type('123')
print type(123).print type(Trick)
output:
<type 'str'>
<type 'int'>
<class '__main__.Trick'>
Copy the code

So you can see that we get our usual STR, int, and an instance object Trick() that we initialized

But the following method, which you may not have seen before, can also be used to dynamically create a class

Type (class name, tuple of the parent class (which can be empty in case of inheritance), dictionary of attributes (name and value)

So how do I use this? I’m going to use this method to create a class and let’s look at the code below, okay

input:
print type('Trick', (), {})

output:
<class '__main__.Trick'> We can also instantiate the class input:print type('trick', (), {})() output: <__main__. Trick object at 0x109283450>Copy the code

This method also initializes the parent class that created the class. It also initializes the class attributes:

input:
class FlyToSky(object):
   pass

pw = type('Trick', (FlyToSky, ), {'laugh_at': 'hahahaha'})
print pw().laugh_at
print pw.__dict__
print pw.__bases__
print pw().__class__
print pw().__class__.__class__

output:
hahahaha
{'__module__': '__main__'.'laugh_at': 'hahahaha'.'__doc__': None}
(<class '__main__.FlyToSky'>,)
<class '__main__.Trick'>
<type 'type'>
Copy the code

I’m going to go through the above in turn, but before I do, I have to introduce two magic methods:

The __class__ method is used to see which object is generated, understanding that everything in Python is an object, and class objects are also objects. If you think about it in the old way, classes are instances of metaclasses, and instance objects are instances of classes. The __bases__ method is used to find out who the parent of an object is. Note in particular that __base__ returns a single parent and __bases__ returns all parent classes as tuples.Copy the code

Ok, so with these two methods let me say what each line means in turn.

Class name, does the class have a parent class (), and class attribute dictionary {})

Initialize an instance of the class, and then try to get the laugh_AT property value of the class property, resulting in hahahaha

Take the class dictionary data of a PW, which is our common class

Get the parent of pw and the result is FlyToSky that we specified

Pw () is initialized to class Trick

Who initializes class trick? That’s the metaclass type

What are metaclasses and simple applications

With all this introduced, we can finally get down to business

What exactly is a metaclass? In layman’s terms, metaclasses are classes that create classes… Does that sound super abstract?

Take a look at this.

Trick = MetaClass()
MyObject = Trick()
Copy the code

We have introduced the above, to play a Trick can be directly like this

Trick = type(‘Trick’, (), {}) What is a metaclass? As I said, a metaclass is a class that creates a class. In other words, it is a class creation factory.

Class __metaclass__ attribute, I believe that anyone willing to learn the details of metaclass, must have seen this thing, and curious about it. Otherwise I don’t know what supports you see here 😂. Using __metaclass__ means that the class is created using the metaclass__ metaclass__ metaclass__ metaclass__ metaclass__ metaclass__ metaclass__ metaclass__ metaclass__ metaclass__ metaclass__.

class Trick(FlyToSky):
   pass
Copy the code

When we created the above class, Python did the following:

Is there an __metaclass__ attribute in Trick? If so, Python creates an in-memory class object named Trick via __metaclass__, which is Trick. If Python does not find __metaclass__, it continues to look for the __metaclass__ attribute in its parent FlyToSky class and tries to create a Trick class object with the __metaclass__ method. If Python can’t find __metaclass__ in any of the parent classes, it doesn’t give up, but looks for the module to see if __metaclass__ is specified. If you still can’t find Trick, use the default type to create Trick.

So what do we put in __metaclass__? The answer is anything that can create a class, type, or anything that uses type or subclasses type.

Custom metaclasses

The purpose of a custom class, as I’ve summarized it, is to intercept the creation of a class, modify some features, and then return the class. Does that sound familiar? Yeah, it feels like a decorator, but a decorator is a function that decorates, the same thing that goes in, gets added to, gets returned.

In addition to the fact that __metaclass__ does not need to be assigned to a formal class, it does need to be a function. To create aclass that uses this metaclass for all module levels, set __metaclass__ at the module level. Let’s try one out

class UpperAttrMetaClass(type):
   def __new__(mcs, class_name, class_parents, class_attr):
       attrs = ((name, value) for name, value in class_attr.items() if not name.startswith('__'))
       uppercase_attrs = dict((name.upper(), value) for name, value in attrs)
       return super(UpperAttrMetaClass, mcs).__new__(mcs, class_name, class_parents, uppercase_attrs)


class Trick(object):
   __metaclass__ = UpperAttrMetaClass
   bar = 12
   money = 'unlimited'

print(Trick.bar)
print(Trick.money)
Copy the code

Implement the singleton pattern in as many ways as possible.

Singleton Pattern is a common software design Pattern whose main purpose is to ensure that only one instance of a class exists. Singletons come in handy when you want only one instance of a class in the entire system.

For example, the configuration information for a server program is stored in a file that the client reads through an AppConfig class. If the contents of the configuration file are used in many places during the program’s run, that is, many places need to create instances of AppConfig objects, this can result in multiple AppConfig instance objects on the system, which can be a serious waste of memory resources, especially if the contents of the configuration file are large. In fact, with a class like AppConfig, we expect only one instance object to exist during the program’s run.

In Python, we can implement the singleton pattern in a number of ways

In fact, Python modules are natural singletons, because modules generate.pyc files when they are first imported, and load.pyc files when they are imported a second time without executing the module code again. Therefore, we only need to define the related functions and data in a module to obtain a singleton object. If we really want a singleton class, consider this:

class Singleton(object):
   def foo(self):
       pass
singleton = Singleton()
Copy the code

Save the above code in the file mysingleton.py, and when you want to use it, import the object from this file directly into another file, which is the singleton object

from a import singleton

Use decorators

def Singleton(cls):
   _instance = {}
   def _singleton(*args, **kargs):
       if cls not in _instance:
           _instance[cls] = cls(*args, **kargs)
       return _instance[cls]
   return _singleton

@Singleton
class A(object):
   a = 1
   def __init__(self, x=0):
       self.x = x

a1 = A(2)
a2 = A(3)
Copy the code

Using class

class Singleton(object):
   def __init__(self):
       pass
   @classmethod
   def instance(cls, *args, **kwargs):
       if not hasattr(Singleton, "_instance"):
           Singleton._instance = Singleton(*args, **kwargs)
       return Singleton._instance
Copy the code

In general, you think this completes the singleton pattern, but this can be problematic when using multiple threads

class Singleton(object):

   def __init__(self):
       pass

   @classmethod
   def instance(cls, *args, **kwargs):
       if not hasattr(Singleton, "_instance"):
           Singleton._instance = Singleton(*args, **kwargs)
       return Singleton._instance
import threading
def task(arg):
   obj = Singleton.instance()
   print(obj)

for i inrange(10): T = threading.Thread(target=task,args=[I,]). Start ()  <__main__.Singleton object at 0x02C933D0> <__main__.Singleton object at 0x02C933D0> <__main__.Singleton object at 0x02C933D0> <__main__.Singleton object at 0x02C933D0> <__main__.Singleton object at 0x02C933D0> <__main__.Singleton object at 0x02C933D0> <__main__.Singleton object at 0x02C933D0> <__main__.Singleton object at 0x02C933D0> <__main__.Singleton object at 0x02C933D0> <__main__.Singleton object at 0x02C933D0>Copy the code

It doesn’t seem to be a problem, because it’s too fast. If you have some IO in the init method, you’ll see the problem. Here’s how to simulate it with time.sleep

We add the following code to the __init__ method above:

def __init__(self):
       import time
       time.sleep(1)
Copy the code

After re-executing the program, the result is as follows

<__main__.Singleton object at 0x034A3410>
<__main__.Singleton object at 0x034BB990>
<__main__.Singleton object at 0x034BB910>
<__main__.Singleton object at 0x034ADED0>
<__main__.Singleton object at 0x034E6BD0>
<__main__.Singleton object at 0x034E6C10>
<__main__.Singleton object at 0x034E6B90>
<__main__.Singleton object at 0x034BBA30>
<__main__.Singleton object at 0x034F6B90>
<__main__.Singleton object at 0x034E6A90>
Copy the code

There’s a problem! The singleton created in the above manner cannot support multithreading

Solution: Lock it! The unlocked part is executed concurrently, and the locked part is executed sequentially, which reduces the speed, but ensures the data security

import time
import threading
class Singleton(object):
   _instance_lock = threading.Lock()

   def __init__(self):
       time.sleep(1)

   @classmethod
   def instance(cls, *args, **kwargs):
       with Singleton._instance_lock:
           if not hasattr(Singleton, "_instance"):
               Singleton._instance = Singleton(*args, **kwargs)
       return Singleton._instance

def task(arg):
   obj = Singleton.instance()
   print(obj)
for i in range(10):
   t = threading.Thread(target=task,args=[i,])
   t.start()
time.sleep(20)
obj = Singleton.instance()
print(obj) The printed result is as follows:  <__main__.Singleton object at 0x02D6B110> <__main__.Singleton object at 0x02D6B110> <__main__.Singleton object at 0x02D6B110> <__main__.Singleton object at 0x02D6B110> <__main__.Singleton object at 0x02D6B110> <__main__.Singleton object at 0x02D6B110> <__main__.Singleton object at 0x02D6B110> <__main__.Singleton object at 0x02D6B110> <__main__.Singleton object at 0x02D6B110> <__main__.Singleton object at 0x02D6B110>Copy the code

This is almost enough, but there is still a small problem, that is, when the program is executed, after time.sleep(20) is executed, the following object is instantiated, it is already a singleton mode, but we still add the lock, which is not very good, and then some optimization, intance method is changed to the following:

@classmethod
   def instance(cls, *args, **kwargs):
       if not hasattr(Singleton, "_instance"):
           with Singleton._instance_lock:
               if not hasattr(Singleton, "_instance"):
                   Singleton._instance = Singleton(*args, **kwargs)
       return Singleton._instance
Copy the code

The Singleton pattern implemented in this way is limited in use and must be instantiated through obj = singleton.instance ().

If obj=Singleton() is used, this is not a Singleton

Based on the __new__ method (recommended), we can see from the above example that when we implement singletons, we need to add locks internally for thread safety

We know that when we instantiate an object, we execute the class’s __new__ method (which calls Object.new by default if we don’t write) to instantiate the object. Then execute the __init__ method of the class to initialize the object, so we can implement the singleton pattern based on this

import threading
class Singleton(object):
   _instance_lock = threading.Lock()

   def __init__(self):
       pass


   def __new__(cls, *args, **kwargs):
       if not hasattr(Singleton, "_instance"):
           with Singleton._instance_lock:
               if not hasattr(Singleton, "_instance"):
                   Singleton._instance = object.__new__(cls)  
       return Singleton._instance

obj1 = Singleton()
obj2 = Singleton()
print(obj1,obj2)

def task(arg):
   obj = Singleton()
   print(obj)

for i in range(10):
   t = threading.Thread(target=task,args=[i,])
   t.start()
Copy the code

Using the Singleton pattern in this way, objects will be instantiated in the same way as they would be instantiated normally. Obj = Singleton()

1. The class is created by type. When the class is created, the __init__ method of type is automatically executed, and the __call__ method of type (class __new__ method, class __init__ method) 2. An object is created by the class. When an object is created, the class’s __init__ method is automatically executed, and the class’s call method () is executed.

import threading
class SingletonType(type):
   _instance_lock = threading.Lock()
   def __call__(cls, *args, **kwargs):
       if not hasattr(cls, "_instance"):
           with SingletonType._instance_lock:
               if not hasattr(cls, "_instance"):
                   cls._instance = super(SingletonType,cls).__call__(*args, **kwargs)
       return cls._instance

class Foo(metaclass=SingletonType):
   def __init__(self,name):
       self.name = name
obj1 = Foo('name')
obj2 = Foo('name')
print(obj1,obj2)
Copy the code