Каковы некоторые Pythonic способы поделиться переменными с объектами, определенными в модуле? - PullRequest
3 голосов
/ 23 января 2010

Я строю модуль, в котором есть множество разнообразных объектов, которыми нужно управлять несколькими переменными.

Если бы это был C, я бы передал объектам указатель на переменную / объект, который я хочу, чтобы они наблюдали, чтобы приложение изменило переменную так, как нужно, и объекты в модуле "увидели бы" новое значение .

В модуле foo.py:

class foo(object):
  def __init__(self, varToWatch):
    self.varToWatch = varToWatch
  def DoSomething(self):
    print self.varToWatch

В приложении bar.py:

import foo
x = "before"
y = foo.foo(x)
y.DoSomething()
x = "after"
y.DoSomething()

Конечно, это не работает, потому что исходное значение x (или ссылка на исходное значение x?) Хранится в y. Вы получаете «до» «до».

Итак, я стараюсь ...

  1. Передача имени глобала и обращение к глобалу по имени в foo. Не надо, потому что глобал находится в пространстве приложения, а не в модуле.
  2. Использование global в пространстве имен foo, но я не знаю, как обращаться к этому пространству по имени переменной, и в настоящее время мы выполняем импорт * из foo, а не import foo. Менять это было бы больно.
  3. Создать словарь в модуле и передать имя ключа в словаре объектам в модуле. Управляйте содержимым словаря из приложения. По сути, я определяю новое глобальное пространство и вставляю в него переменные. Я не вижу причин, по которым это не сработает, но мне это неопрятно. Это я не знаком с Python?

Я не нахожу, что красивые решения, которые я привык видеть, легко приходят с Python, поэтому я подозреваю, что что-то упустил.

Предложения

ТИА, - Тим.

Ответы [ 5 ]

3 голосов
/ 23 января 2010

Чтобы перехватить все повторные привязки имени, вам нужно передать как пространству имен , так и определенное имя, которое будет использоваться в качестве ключа внутри него. Хорошо использовать выделенное dict в качестве пространства имен, но dict для всего модуля (который можно получить как globals()) или для любого конкретного объекта o, для которого вы хотите наблюдать атрибут (используйте vars(o)) так же хорошо.

Итак:

class foo(object):
  def __init__(self, namespace, name):
    self.namespaceToWatch = namespace
    self.nameToWatch = name
  def DoSomething(self):
    print self.namespaceToWatch[self.nameToWatch]

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

import foo
x = "before"
y = foo.foo(globals(), 'x')
y.DoSomething()
x = "after"
y.DoSomething()
3 голосов
/ 23 января 2010

Хитрость заключается в использовании изменяемого типа. Строки являются неизменяемыми, поэтому при изменении строки создается новый строковый объект в новом пространстве памяти. Однако если вы ссылаетесь на список и добавляете его в список, пространство памяти не изменится. Адаптируй свой код ...

>>> class Foo(object):
...     def __init__(self, var):
...         self.var = var
...     def show(self):
...         print self.var
... 
>>> x = ['before']
>>> y = Foo(x)
>>> y.show()
['before']
>>> x.append('after')
>>> y.show()
['before', 'after']
3 голосов
/ 23 января 2010

Как насчет помещения управляющих переменных в отдельный модуль - скажем, settings - и выполнения отдельного import settings? Затем ваши объекты могут наблюдать за settings.bar и settings.quux, в то же время делая from foo import *, чтобы загромождать ваше основное пространство имен. ;-) Вы также можете использовать глобальный объект для хранения ваших настроек, в основном с тем же эффектом.

Конечно, с такими проблемами, как этот, нужно задаться вопросом, не поможет ли реструктуризация вещей, чтобы сделать import foo, в долгосрочной перспективе ... Невозможно судить, не зная проблемы, поэтому я оставь эту проблему тебе.

1 голос
/ 23 января 2010

В комментарии к решению Алекса Мартелли Тброберг спрашивает: «Что если стороны согласны использовать пространство имен модуля? Как приложение может получить пространство имен модуля? "

Вот как вы можете установить пространство имен (по умолчанию) в качестве пространства имен вызывающего абонента:

import inspect
class foo(object):
    def __init__(self, name, namespace=None):
        self.namespaceToWatch = (namespace if namespace else
                                 inspect.currentframe().f_back.f_globals)
        self.nameToWatch = name
    def DoSomething(self):
        print self.namespaceToWatch[self.nameToWatch]

Обратите внимание: я изменил порядок аргументов, поэтому namespace теперь является необязательным аргументом.

inspect.currentframe() возвращает текущий кадр внутри foo.__init__.

inspect.currentframe().f_back возвращает предыдущий кадр, из которого вызывается foo('x').

inspect.currentframe().f_back.f_globals возвращает глобальное пространство имен этого кадра. Это делает

y = test.foo('x')

эквивалентно

y = test.foo('x', globals())
1 голос
/ 23 января 2010

Ваша проблема здесь в том, что в переменных C данные являются объектами. В Python переменные всегда являются ссылками на объекты (которые являются данными). Вы можете легко наблюдать за объектом, но не за переменной. Например, x = "after" фактически "отменяет связь" объекта before с x и связывает новый объект ("after"). Это означает, что x и foo.varToWatch больше не ссылаются на один и тот же объект.

Если, с другой стороны, вы изменяете объект, на который ссылается x, и x, и varToWatch по-прежнему ссылаются на один и тот же объект, и изменение отражается. Примером может быть, если x является изменяемым типом, как словарь - изменение x в этом случае будет отражаться в varToWatch, если вы на самом деле не делаете присваивание.

...