Chapter 3 Python Basics (advanced function decorator)

This article has 100 million points about yellow!

You are a back-end development engineer of a video website. Your website has the following sections.

def home():
    print("---home page----")
def america():
    print("----European and American Zone----")
def japan():
    print("----Japan Korea zone----")
def henan():
    print("----Henan special area----")

At the beginning of the launch of the video, in order to attract users, you adopted a free policy. All videos were watched for free, which quickly attracted a large number of users. After a period of free, the company can't afford the huge bandwidth cost every day, so it is ready to charge for several popular sections, including "Europe and America" and "Henan". After you get this demand, After thinking about it, if you want to charge, you must first let the user authenticate. After the authentication is passed, you can judge whether the user is a VIP paying member. Let's see if it's a VIP or not. You think this requirement is very simple, because to authenticate multiple sections, you should extract the authentication function and write a separate module, and then call it in each section. You can gently realize the following functions.

account = {
    "is_authenticated":False,# Change this to True when the user logs in
    "username":"alex", # Pretend this is the user information stored in the DB
    "password":"abc123" # Pretend this is the user information stored in the DB
}
def login():
    if account["is_authenticated"] is False:
        username = input("user:")
        password = input("pasword:")
        if username == account["username"] and password == account["password"]:
            print("welcome login....")
            account["is_authenticated"] = True
        else:
            print("wrong username or password!")
    else:
        print("The user has logged in and passed the authentication...")
def home():
    print("---home page----")
def america():
    login()  # Add validation before execution
    print("----European and American Zone----")
def japan():
    print("----Japan Korea zone----")
def henan():
    login()  # Add validation before execution
    print("----Henan special area----")
home()
america()
henan()

At this time, you confidently submit this code to your TEAM LEADER for review. Unexpectedly, the code was called back within 5 minutes. TEAM LEADER gave you feedback that I now have many modules that need to be added with authentication modules. Although your code has realized the function, you need to change the source code of each module to be added with authentication, This directly violates the principle of "open closed" in software development. In short, it stipulates that the implemented function code should not be modified, but can be extended, that is:

  • Closed: implemented function code blocks should not be modified
  • Open: open to the extension of existing functions

It's the first time you've heard of this principle. I wipe it. I feel the gap between the wild programmer and the regular army again. BUT ANYWAY, how can the boss achieve this? How to add the authentication function without changing the original function code? You can't think of a way to think for a moment, so you have to go home with this problem and continue to hold it. Your daughter-in-law is not at home. You visit Lao Wang's house next door. You happen to be quiet. You accidentally think of a solution without changing the source code. When you studied with King Jinhe of Shahe, I remember that he taught you that high-order function is to pass a function as a parameter to another function. At that time, the king said that one day, you will use it. Unexpectedly, this knowledge point suddenly popped out of your mind. I just need to write an authentication method. Every time I call the function to be verified, Just pass the function name of this function as a parameter to my verification module. Ha ha, you are as smart as me. If you rewrite the previous code.

account = {
    "is_authenticated":False,# Change this to True when the user logs in
    "username":"alex", # Pretend this is the user information stored in the DB
    "password":"abc123" # Pretend this is the user information stored in the DB
}
def login(func):
    if account["is_authenticated"] is False:
        username = input("user:")
        password = input("pasword:")
        if username == account["username"] and password == account["password"]:
            print("welcome login....")
            account["is_authenticated"] = True
        else:
            print("wrong username or password!")
    if account["is_authenticated"] is True:  # Mainly changed this
        func() # If the authentication is successful, execute the incoming function
def home():
    print("---home page----")
def america():
    print("----European and American Zone----")
def japan():
    print("----Japan Korea zone----")
def henan():
    print("----Henan special area----")
home()
login(america) # If you need to verify, call login and pass the function to be verified to login as a parameter
login(henan)

You are very happy that you have finally fulfilled the boss's requirements and added verification to the function without changing the original function code. At this time, your daughter-in-law comes back and is followed by Lao Wang. Your two families have a very good relationship. Lao Wang often comes to visit. Lao Wang is also a code farmer. You share the code you wrote with him. He is excited to praise you NB after reading it. I didn't expect it. After reading it, Lao Wang, I didn't praise you. I picked up your son, smiled and said, you'd better change the code, or you'll be fired, WHAT? He will be dismissed. He has realized the function. Lao Wang said, yes, your function has been realized, but you have made another big taboo. WHAT big taboo? You have changed the calling method. Think about it. Now every module that needs authentication must call your login() method and pass its own function name to you. People didn't call it like this before. Imagine that if there are 100 modules that need authentication, these 100 modules have to change the calling method. So many modules must be written by more than one person, Let everyone modify the calling method to add authentication, and you will be scolded to death.... You think Lao Wang is right, but the question is, how can we add authentication without changing the original function code and the original calling method? After thinking hard for a while, you still can't think of it. Lao Wang is teasing your son. You said, Lao Wang, give me some ideas. I really can't think of it. Lao Wang asked with his back to you,

Lao Wang: have you studied anonymous functions?

You: Yes, yes, it's lambda

Lao Wang: what is the difference between lambda and normal function?

You: the most direct difference is that you need to write a name when defining a normal function, but lambda doesn't

Lao Wang: that's right. After the lambda is set, can you name it for multiple calls?

You: Yes, it can be written as plus = lambda x:x+1. In this way, you can call plus later, but it will lose the meaning of lambda. It's clearly called an anonymous function. What's the use of your name?

Lao Wang: I don't want to discuss its meaning with you. I want to let you understand a fact through this

Lao Wang said as he picked up your son's drawing board and wrote the following code on it:

def plus(n):
    return n+1
plus2 = lambda x:x+1

Lao Wang: do these two expressions mean the same thing?

You: Yes

Lao Wang: I named lambda x:x+1 plus2. Is it equivalent to def plus2(x)?

You: I wipe, don't say, it's true, but Lao Wang, what do you want to explain?

Lao Wang: nothing. I just want to tell you that assigning variable names to functions is like def func_name has the same effect. For example, in the following plus(n) function, you can use the plus name when calling, and you can also give another name, such as

calc = plus
calc(n)

Do you understand what I'm trying to convey? You:........... This...... Um..... Not very.... Understand?

Lao Wang:.... This..... ha-ha...... ok Let me give you a click. The following code you wrote earlier calls authentication

home()
login(america) #If you need to verify, call login and pass the function to be verified to login as a parameter
# home()
# america()
login(henan)

Lao Wang: the reason why you changed the calling method is that the user needs to execute login(henan) every time. In fact, it's OK to change it a little

home()
america = login(america)
henan = login(henan)

Lao Wang: in this way, when others call henan in the future, it is actually equivalent to calling login(henan). After passing the verification in login, they will automatically call the henan function.

You: I wipe it. It's really alas..., Lao Wang, you nb... But wait, I wrote it like this. When the user calls, it should look like this

home()
america = login(america) #Here you are equivalent to replacing the American function
henan = login(henan)
#The user still writes when calling
america()

But the problem is that your america = login(america) will execute america itself before the user calls it...., You should wait until my user calls it. If you don't believe me, I'll try to show you...

Lao Wang: ha ha, you're right. This problem will arise, but do you think there is a solution?

You: I wipe, you mean thinking, big brother... I don't know what to do next...

Lao Wang: forget it, I guess you can't think of it... Have you learned nested functions?

You: yes, and then?

Lao Wang: to realize that the america = login(america) you wrote at the beginning does not trigger the execution of your real America function, you only need to define another layer of function in this login. The first call to america = login(america) only calls the outer login. Although this login will execute, it will not trigger authentication, Because all the authentication codes are encapsulated in the newly defined functions of the login inner layer, login only returns the function name of the inner layer function, so that the inner layer function will be called the next time america() is executed...

You:...... what? What do you mean, I was forced...

Lao Wang: let me show you the code..

account = {
    "is_authenticated":False,# Change this to True when the user logs in
    "username":"alex", # Pretend this is the user information stored in the DB
    "password":"abc123" # Pretend this is the user information stored in the DB
}
def login(func):
    def inner(): # Define another layer of functions
        if account["is_authenticated"] is False:
            username = input("user:")
            password = input("pasword:")
            if username == account["username"] and password == account["password"]:
                print("welcome login....")
                account["is_authenticated"] = True
            else:
                print("wrong username or password!")
        if account["is_authenticated"] is True:
            func()
    return inner  # Note that only the memory address of inner is returned here, and no execution is performed
def home():
    print("---home page----")
def america():
    print("----European and American Zone----")
def japan():
    print("----Japan Korea zone----")
def henan():
    print("----Henan special area----")
home()
america = login(america) # The login operation returns the internal memory address. Internal at 0x101762840 >
henan = login(henan)  # .inner at 0x102562840>
america()  # Equivalent to executing inner()
henan()

At this time, you carefully read the code written by Lao Wang, and you feel that Lao Wang is really not an ordinary person. You can even think of such strange skills..., Silently thank God for giving you a big neighbor.

You: Lao Wang, your posture is nb very original? At this time, your daughter-in-law burst out laughing. You don't know she smiled a ball...

Lao Wang: hehe, this is not my original creation. Of course, this is a commonly used play method in development. It is called grammar sugar. The official name is "decorator". In fact, the above writing method can be simpler, and the following code can be removed

america = login(america) # This time, login returns the memory address of inner
henan = login(henan)  # .inner at 0x102562840>

Just add the following code to the function you want to decorate

@login
def america():
    print("----European and American Zone----")
def japan():
    print("----Japan Korea zone----")
@login
def henan():
    print("----Henan special area----")

The effect is the same.

You are happy to play with the new posture taught by Lao Wang. You add a parameter to your "Henan special area" section. Then, the result is wrong..

You: Lao Wang, Lao Wang, how can you pass a parameter?

Lao Wang: that's inevitable. When you call henan, it's actually equivalent to the called login. When you call henan for the first time, henan = login(henan), login returns the memory address of the inner. The second time the user calls henan(5), it's actually equivalent to calling inner, but you don't set parameters when defining your inner, but you pass a parameter to him, so naturally an error is reported

You: but my section needs to pass parameters. You can't pass it if you don't let me pass it...

Lao Wang: I didn't say I wouldn't let you pass it. Just make a little change..

Lao Wang: just try again.

You: sure enough, the great God is the great God.. However, what if there are multiple parameters?

Lao Wang:.... Brother, don't let me teach you everything. Haven't you learned non fixed parameters? args,*kwargs…

You: Oh... Can you do that?, nb, I'll try again. You can't extricate yourself from this new way of playing. You didn't notice that Lao Wang has left. Your daughter-in-law told you that in order not to disturb you to work overtime, you took your children to live with her sister tonight. You think her daughter-in-law is really considerate. Finally, you finally solved all your needs and fully followed the open-closed principle. The final code is as follows.

account = {
    "is_authenticated":False,# Change this to True when the user logs in
    "username":"alex", # Pretend this is the user information stored in the DB
    "password":"abc123" # Pretend this is the user information stored in the DB
}
def login(func):
    def inner(*args,**kwargs): # Define another layer of functions
        if account["is_authenticated"] is False:
            username = input("user:")
            password = input("pasword:")
            if username == account["username"] and password == account["password"]:
                print("welcome login....")
                account["is_authenticated"] = True
            else:
                print("wrong username or password!")
        if account["is_authenticated"] is True:
            func(*args,**kwargs)
    return inner  # Note that only the memory address of inner is returned here, and no execution is performed
def home():
    print("---home page----")
@login
def america():
    print("----European and American Zone----")
def japan():
    print("----Japan Korea zone----")
@login
def henan(vip_level):
    if vip_level < 3:
        print("----Ordinary member of Henan special zone----")
    else:
        print("Welcome to the distinguished Henan accent RMB Player private community".center(50,"-"))
        print("Recharge another 500 to get the actor's micro signal, and the door to happiness is about to open".center(50," "))
# home()
# america = login(america) # The login operation returns the internal memory address. Internal at 0x101762840 >
# henan = login(henan)  # .inner at 0x102562840>
america()  # Equivalent to executing inner()
henan(5)

Posted on Fri, 26 Nov 2021 03:48:38 -0500 by Mr Mako