Динамическое присвоение глобальных переменных - PullRequest
5 голосов
/ 15 ноября 2010

Я новичок в python, и у меня много проблем с использованием инструкции global.Вот пример кода:

mouse = "a"
background = "b"

list_ab = [mouse, background]

def func ():
    for item in list_ab:
        global item  # I want to modify the GLOBAL item, which is mouse
                     # and background.
        item = "modified"
    print mouse  # must be "modified" not "a"
    print background  # must be "modified" not "b"

Это проблема.Как я могу решить это?

Ответы [ 6 ]

6 голосов
/ 15 ноября 2010

Ваша проблема в том, что Python работает не так, как вы думаете.
Поэтому я постараюсь объяснить, что происходит в вашем коде, строка за строкой.

mouse = "a"

Назначает строку "a"на имя mouse.

background = "b"

Назначает строку "b" имени background.

list_ab = [mouse, background]

Назначает два объекта, на которые ссылаются имена mouse и background, к списку list_ab.
Как мы уже знаем, они являются константами "a" и "b".Таким образом, вы можете просто написать:

list_ab = ["a", "b"]

Теперь цикл

for item in list_ab:

присваивает каждому объекту в списке имя item.
Для первой итерации цикла этоозначает item = "a".

Строка

    global item # I want to modify the GLOBAL item, which is mouse ad background

не имеет смысла, потому что она пытается сообщить Python, что имя item является глобальным, в то время как такой глобальной переменной нетобъявляется.

И для наиболее запутанного поведения в строке

    item = "modified"

вы должны просто понимать, что, пока он присваивает строку «измененный» имени item, строки «а»и "b" все те же, и все еще присваиваются списку list_ab (и именам mouse и background, которые вы тоже не трогали).

Само имя item живет только в той области, где оно было объявлено, когда цикл «for» заканчивается, он уничтожается.Он также переназначается для каждой итерации со следующим элементом из list_ab.

Подводя итог:

Если вы хотите назначить «измененную» строку дляэлементы в списке, делайте это напрямую:

list_ab[0] = "modified"
list_ab[1] = "modified"

Если вам нужно изменить переменную, объявленную в глобальной области видимости, сделайте это:

mouse = "a"

def func():
    global mouse   # tell Python that you want to work with global variable
    mouse = "modified"

func()
print mouse # now mouse is "modified"

Пример на основе первогоПересмотр вопроса:

Вместо

background = g_base("bg.jpg") # g_base class instance
mouse = g_base("mouse.png",alpha=1) # g_base class instance

imgs_to_load = [mouse, background]

def Image_loader (img):
    # ..... code to load the image and convert it into pygame surface...
    return img_load

def main ():
    for image in img_to_load:
        global image
        image = Image_loader (img)
        print image # if I print image, it is a pygame surface

    print background # But here, outside the loop, image is still a g_base instance ?!?!
    print mouse # " "   

main()

Вы можете сделать:

imgs_to_load = [g_base("bg.jpg"), g_base("mouse.png",alpha=1)] # list with a g_base class instances

def Image_loader(img):
    # ..... code to load the image and convert it into pygame surface...
    return img_load

def main ():
    imgs_in_pygame_format = [] # create an empty list
    for image in imgs_to_load:
        loaded_image = Image_loader(image) 
        imgs_in_pygame_format.append(loaded_image) # add to the list 

    for image in imgs_in_pygame_format:
        print image # every image in the list is a pygame surface

    # or
    print image[0]
    print image[1]

main()

Или, если вы хотите сослаться на изображения по имени, выможно поместить их в словарь:

imgs_to_load = {} 
imgs_to_load["bg"] = g_base("bg.jpg")
imgs_to_load["mouse"] = g_base("mouse.png",alpha=1)

imgs_in_pygame_format = {} # create a global dictionary for loaded images

def main ():
    global imgs_in_pygame_format # import that global name into the local scope for write access
    for name, data in imgs_to_load.items():
        imgs_in_pygame_format[name] = Image_loader(data) # add to the dictionary

    for image in imgs_in_pygame_format:
        print image # every image in the dictionary is a pygame surface

    # or    
    print imgs_in_pygame_format["bg"]
    print imgs_in_pygame_format["mouse"]

main()

Но самое главное, глобальные переменные - плохая идея .Лучшим решением было бы использовать класс:

def Image_loader(img):
    # ..... code to load the image and convert it into pygame surface...
    return img_load

class Image:
    def __init__(self, image):
        self.data = Image_loader(image)

def main ():
    bg = Image(g_base("bg.jpg"))
    mouse = Image(g_base("mouse.png",alpha=1))

    print bg.data
    print mouse.data

main()

Дополнительные примеры см. В Учебник по Python .

Надеюсь, это поможет, и добро пожаловать в StackOverflow!

4 голосов
/ 15 ноября 2010

Как я уже сказал в своем комментарии к вашему вопросу, list_ab не содержит ссылок на mouse и background. Вы можете имитировать это до некоторой степени в этом случае, поместив их имена в список, например:

mouse = "a"
background = "b"

list_ab = ['mouse', 'background']

def func():
    for name in list_ab:
        globals()[name] = "modified"
    print mouse # must be "modified" not "a"
    print background # must be "modified" not "b"

func()
# modified
# modified

globals() возвращает похожий на словарь объект, который представляет нелокальные привязки имен в тот момент выполнения вашего скрипта / модулей. Важно отметить, что list_ab не изменяется циклом for в func(). Также вы должны знать, что это только иллюстрация одного простого способа заставить ваш пример кода работать так, как вы хотели, а не особенно хорошего или универсального способа выполнения таких вещей.

2 голосов
/ 15 ноября 2010

У вас две разные проблемы, и одна из них возникает дважды. Во-первых, вы пытаетесь использовать global для элементов списка, который бесполезен. Вы уже можете сделать:

def func():
    list_ab[0] = 'modified'
    list_ab[1] = 'modified'

Это изменит значения, на которые ссылается list_ab, что дальше, чем ваш код получает сейчас. Это работает, потому что вы не изменяете привязку, представленную list_ab, и поэтому она не должна быть глобальной Вы уже можете прочитать глобальную индексацию, и она представляет собой просто поиск элементов и сама по себе не перезаписывает никаких привязок. Однако в действительности это не изменит значения, на которые ссылаются a и b

.

Во-вторых, когда вы привязываете первый и второй индексы list_ab к a и b, он создает совершенно новые привязки, так что изменение значений привязок в списке не приводит к изменению значений. упоминаются a и b. Если вы хотите сделать это, вам нужно сделать это напрямую.

def func():
    global a, b
    a = 'modified'
    b = 'modified'

Вы снова сталкиваетесь со второй проблемой, когда пытаетесь изменить элементы list_ab, перебирая их. Вы можете увидеть это более четко с кодом

def func():
    list_ab = ['a', 'b']
    for item in list_ab:
        item = 'modified'
    print list_ab        # prints ['a', 'b']

Опять же, это происходит потому, что вы создаете новую привязку к значению, а затем модифицируете эту привязку вместо исходной.

Если вам нужно изменить список на месте во время итерации, вы можете сделать

def func():
    list_ab = ['a', 'b']
    for i in xrange(len(list_ab)): 
        list_ab[i] = 'modified'

Таким образом, в настоящее время необходимо обновить a, b и list_ab и сохранить ваши текущие шаблоны (т. Е. Не вводить понимания), вам необходимо сделать:

def func():
    global a, b
    for i in xrange(len(list_ab)):
        list_ab[i] = 'modified'
    a = 'modified'
    b = 'modified'
    print list_ab
    print a, b

Это выглядит довольно некрасиво. Зачем вам нужно хранить их как свободные переменные и в списке? Разве список не достаточно хорош? Если вам нужен доступ к ним по имени, вы можете использовать словарь:

dict_ab = {'a': 'a', 'b': 'b'}
for key in dict_ab:
    dict_ab[key] = modified

print dict_ab['a']
print dict_ab['b']
2 голосов
/ 15 ноября 2010

Я не уверен, что вы пытаетесь сделать.Ключевое слово global предназначено для импорта глобального имени в текущую область, а не для того, чтобы сделать локальное имя глобальным.

1 голос
/ 15 ноября 2010

Проблема в том, что вы пытаетесь обновить переменные на месте, но ваши функции возвращают новые экземпляры. Попробуйте что-то вроде этого:

def main ():
    converted_images = []
    for image in [mouse, background]:
        converted_images.append(Image_loader(img))
    # now re-assign the pygame surfaces to the old names
    background, mouse = converted_images

И если вы хотите быть особенно лаконичным, вот строчка, которая делает то же самое:

background, mouse = [Image_loader(img) for img in [background, mouse]]

Но на самом деле, если у вас есть только два изображения, возможно, имеет смысл сделать это явно:

background = Image_loader(background)
mouse = Image_loader(mouse)
0 голосов
/ 15 ноября 2010

Я тоже не уверен, что вы пытаетесь сделать, но вот пример использования global:

value = 7

def display():
    print value # global values are read-only any time

def increment():
    global value # must declare global to write it
    value += 1

def local():
    value = 9 # this is a local value
    print value

increment() # changes global value to 8
display()   # displays it
local()     # changes local value to 9 and displays it
print value # displays global value (still 8)
...