简体   繁体   中英

Change scope of local variable by decorators?

I have a method that set the name variable but there isn't global name at the first line of it, It uses local name variable so It never changes the global name variable. Is there any way that by decorating set_name force it to use global name ?

name = "John"

def set_name():
    name = "Sara"

def print_name():
    global name
    print(name)

def decorator(func):
    def wrapper(*args, **kwargs):
        ## Problem:
        # Force set_name to use global 'name'
        func(*args, **kwargs)

    return wrapper

print_name()
#Output: John

decorator(set_name)()

print_name()
# Prints John, but I want to prints Sara

No, unless the decorator takes the function's code (bytecode from the function's code object, or source code using inspect ), changes it, creates a new code object from the changed code and replaces the function's code with that. Which is, of course, highly unreliable, hacky, implementation-specific and deep black magic. Just add global name (or eliminate the global variable altogether).

The short answer is: no.

A bit extended answer: no, not in a portable way. Technically, you could play around with the CPython bytecode, but

  • that will not run on any other interpreter,
  • nobody will be able to understand that code,
  • the headache won't be worth it.
name = "John" #global name

and name inside set_name():

name = "Sara" #local name

To use global name inside function set_name() do like:

def set_name():
   global name
   name = "Sara"

Define a class that wraps name :

class Person(object):
    def __init__(self):
        self.name = "John"

    def set_name(self):
        self.name = "Sara"

    def print_name(self):
        print(name)

p = Person()
p.print_name()   # Prints John
p.set_name()     # Changes name to Sara
p.print_name()   # Prints Sara

A class which hard-codes its attribute values like this is, of course, a little silly, but this demonstrates the purpose of a class: to encapsulate data ( name ) and the functions ( set_name , print_name ) that operate on it.

Not sure, why you are trying to decorate the setter.

You can do something like this, but I would not recommend it as a usual style of coding:

name = "John"

def print_name():
    print name

def decorator(func, tmp_name):
    def wrapper(*args, **kwargs):
        global name
        old_name = name
        name = tmp_name
        func(*args, **kwargs)
        name = old_name
    return wrapper

print_name()
decorator(print_name, 'Sarah')()
print_name()

Output:

John
Sarah
John

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM