2010-07-08 3 views
7

У меня есть куча функций (вне любого класса), где я установил для них атрибуты, например funcname.fields = 'xxx'. Я надеялся, что я мог бы получить доступ к этим переменным из функции с self.fields, но, конечно, он говорит мне:"self" внутри простая функция?

глобальное имя «я» не определен

Итак ... что я могу делать? Есть ли какая-то магическая переменная, к которой я могу получить доступ? Мне нравится __this__.fields?


Несколько человек спросили «почему?». Вы, вероятно, не согласитесь с моими рассуждениями, но у меня есть набор функций, которые все должны использовать одну и ту же подпись (принимать только один аргумент). По большей части этого одного аргумента достаточно, чтобы выполнить требуемое вычисление. Однако в нескольких ограниченных случаях необходима дополнительная информация. Вместо того чтобы заставить каждую функцию принимать длинный список в основном неиспользуемых переменных, я решил просто установить их в функции, чтобы их можно было легко игнорировать.

Хотя теперь мне кажется, что вы можете использовать **kwargs в качестве последнего аргумента, если вам не нужны дополнительные аргументы. О хорошо ...

Редактировать: На самом деле, некоторые из функций, которые я не писал, и предпочел бы не изменять, чтобы принимать дополнительные аргументы. Благодаря «передаче» дополнительных аргументов в качестве атрибутов мой код может работать как с моими пользовательскими функциями, которые используют преимущества дополнительных аргументов, так и с сторонним кодом, который не требует дополнительных аргументов.

Спасибо за быстрые ответы :)

+0

Как видите, это можно сделать, но лучший вопрос: почему? * –

+0

@Wayne: Смотрите обновление. – mpen

+0

'** kwargs' * * * возможно, сделает ваше намерение более четким ... и, безусловно, более« pythonic » –

ответ

2

Вы не можете сделать это «функция доступа свои собственные атрибуты» правильно во всех ситуациях - смотрите подробности здесь how can python function access its own attributes? - но вот быстрый демонстрация:

>>> def f(): return f.x 
... 
>>> f.x = 7 
>>> f() 
7 
>>> g = f 
>>> g() 
7 
>>> del f 
>>> g() 
Traceback (most recent call last): 
    File "<interactive input>", line 1, in <module> 
    File "<interactive input>", line 1, in f 
NameError: global name 'f' is not defined 

В основном большинство методов прямо или косвенно зависят от доступ к объекту функции путем поиска по имени в глобалях; и если имя оригинальной функции удалено, это перестает работать. Существуют и другие способы осуществления этого, такие как определение класса или фабрики, но, благодаря вашим объяснениям, ясно, что вам это действительно не нужно.

Просто сделайте упомянутое ключевое слово кетчуп все аргументы, например так:

def fn1(oneArg): 
    // do the due 

def fn2(oneArg, **kw): 
    if 'option1' in kw: 
     print 'called with option1=', kw['option1'] 
    //do the rest 

fn2(42) 
fn2(42, option1='something') 

Не уверен, что вы имеете в виду в вашем комментарии обращения TypeError - что не возникнет при использовании ** кВт. Этот подход очень хорошо работает для некоторых функций системы python - проверьте min(), max(), sort(). Недавно sorted(dct,key=dct.get,reverse=True) пришел очень удобно для меня в CodeGolf challenge :)

+0

Это произойдет, если я опустил '** kw'. У меня есть некоторые более старые функции, которые принимают только один аргумент. Если я передам им целую кучу, они будут бросать 'TypeError'. Я могу поймать исключение, а затем снова вызвать функцию, но только с одним аргументом. Во всяком случае, это довольно тщательный ответ, и вы положили туда хорошее предупреждение, о котором другие не говорили. Не понял, что я еще не задал этот вопрос ... так поздравляю! – mpen

+0

Спасибо. Возможно, вы можете ввести «отсутствующий» аргумент в определении более старого fn с помощью декоратора, как в этом недавно опубликованном ответе http://stackoverflow.com/questions/3109289#3209862 –

2

Пример:

>>> def x(): pass 
>>> x 
<function x at 0x100451050> 
>>> x.hello = "World" 
>>> x.hello 
"World" 

Вы можете установить атрибуты функций, поскольку это просто объекты, но я на самом деле никогда не видел что-то подобное в реальном коде.

Плюс. self - это не ключевое слово, просто другое имя переменной, которое является конкретным экземпляром класса. self передается неявно, но получено явно.

+0

Да, но я спрашивал, как получить доступ к 'x.hello' из * внутри *' x() '. Но я думаю, что ответ «одинаковый». Мне не нравится, что имя функции связано так. – mpen

1

Если вы хотите установить глобальные параметры для вызываемой «вещи», вы всегда можете создать класс и реализовать метод __call__?

+0

Вот что я собирался сделать ... но тогда вам понадобится дополнительный набор '()' s для его инициализации в первую очередь. – mpen

11

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

Вы должны почти всегда предпочитают передать аргументы функций по настройке свойств для ввода, но если вы должны, вы можете сделать это, используя фактические функции имя для доступа к переменным внутри него:

def a: 
    if a.foo: 
     #blah 

a.foo = false 
a() 

см python function attributes - uses and abuses когда это пригодится. : D

+0

Есть некоторые вещи, которые вы не можете избежать. –

0

На что именно надеется «я», если функция определена вне любого класса? Если вашей функции требуется некоторая глобальная информация для правильной работы, вам необходимо отправить эту информацию в функцию в форме аргумента.

Если вы хотите, чтобы ваша функция была контекстной, вам необходимо объявить ее в пределах области действия объекта.

3
def foo(): 
    print(foo.fields) 
foo.fields=[1,2,3] 

foo() 
# [1, 2, 3] 

Нет ничего плохого в добавлении атрибутов в функции. Многие воспоминания используют это для кэширования результатов самой функции.

Например, обратите внимание на использовании func.cache:

from decorator import decorator 
@decorator 
def memoize(func, *args, **kw): 
    # Author: Michele Simoniato 
    # Source: http://pypi.python.org/pypi/decorator 
    if not hasattr(func, 'cache'): 
     func.cache = {} 
    if kw: # frozenset is used to ensure hashability 
     key = args, frozenset(kw.iteritems()) 
    else: 
     key = args 
    cache = func.cache # attribute added by memoize 
    if key in cache: 
     return cache[key] 
    else: 
     cache[key] = result = func(*args, **kw) 
     return result 
1

Там нет особого способа, внутри тела функционального режима, а для обозначения функции объекта, код которого выполняется. Проще всего просто использовать funcname.field (с именем funcname, являющимся именем функции в пространстве имён, в котором вы указываете, в противном случае это будет тяжелее).

1

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

То есть, я думаю, что это на самом деле делает то, что вы просили:

import inspect 

_code_to_func = dict() 
def enable_function_self(f): 
    _code_to_func[f.func_code] = f 
    return f 

def get_function_self(): 
    f = inspect.currentframe() 
    code_obj = f.f_back.f_code 
    return _code_to_func[code_obj] 

@enable_function_self 
def foo(): 
    me = get_function_self() 
    print me 

foo() 
1

Хотя я согласен с остальными, что это, вероятно, не хороший дизайн, вопрос же заинтриговать меня. Вот мое первое решение, которое я могу обновить, как только я получу декораторы. Как он стоит, он опирается в значительной степени на возможность чтения из стека, который не может быть возможно во всех реализациях (кое-что о sys._getframe() не обязательно присутствующей ...)

import sys, inspect 

def cute(): 
    this = sys.modules[__name__].__dict__.get(inspect.stack()[0][3]) 
    print "My face is..." + this.face 

cute.face = "very cute" 
cute() 

Что вас думать? : 3

+0

Ну, это определенно симпатично ... и я * am * использую подобный «хак» в моем коде, который уже использует преимущество стека. Однако ... ты прав.Это определенно должен быть декоратором;) И я думаю, что определение названия функции - это меньшее из зла. – mpen

1

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

class Generic_Object(object): 
    pass 

def foo(a1, a2, self=Generic_Object()): 
    self.args=(a1,a2) 
    print "len(self.args):", len(self.args) 
    return None 

... как вы можете видеть, что это позволит вам использовать «я», как вы описали. Вы не можете использовать «объект()» напрямую, потому что вы не можете использовать значения «monkey patch (*)» в экземпляре object(). Однако обычные подклассы объекта (такие как Generic_Object(), которые я здесь показал) могут быть «обезврежены»

Если вы хотите всегда вызывать свою функцию со ссылкой на какой-то объект в качестве первого аргумента, который был бы возможен. Сначала вы можете поместить аргумент по умолчанию, за которым следуют параметры * args и optional ** kwargs (через которые любые вызовы или словари опций могут быть переданы во время вызовов этой функции).

Это, как я сказал отвратительно уродливый. Пожалуйста, никогда не публикуйте какой-либо код, подобный этому, или не сообщайте его никому в сообществе Python. Я только показываю его здесь как своего рода странное воспитательное упражнение.

Метод экземпляра подобен функции в Python. Однако он существует в пространстве имен класса (таким образом, он должен быть доступен через экземпляр ... myobject.foo() например), и он вызывается со ссылкой на «self» (аналогичный «этому» указателю на C++), поскольку первый аргумент. Также есть процесс разрешения метода, который заставляет интерпретатор искать пространство имен экземпляра, затем он является классом, а затем каждый из родительских классов и т. Д. ... через дерево наследования.

Несвязанная функция вызывается с любыми аргументами, которые вы передаете ей. В списке аргументов не может быть какой-либо автоматически привязанной ссылки на объект/экземпляр объекта. Таким образом, запись функции с начальным аргументом с именем «я» не имеет смысла. (Это законно, потому что Python не выделяет никакого особого значения для имени «я». Но бессмысленно, потому что вызывающие функции вашей функции должны были вручную указывать какую-либо ссылку на объект в список аргументов, и совсем не ясно, что это должно быть Просто какой-то причудливый «Generic_Object», который затем плавает в глобальном пространстве переменных?).

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

  • («обезьяна исправления» это термин, используемый для описания прямого манипулирования объектов атрибутов - или «переменный экземпляр» с помощью кода, который не является частью иерархии классов которых объект является экземпляром).
+0

Naw ... Я думаю, что я знал, как я работал, я просто не думал об этом, когда я начал по этому пути :) Большинство других объектов, с которыми я работал на самом деле *, являются * классами, поэтому он работает до этого момента. Затем я решил сделать все творческое и подумал: «ну ... что, если я не хочу создавать целый класс именно для этого ?!» и ... да. Во всяком случае, я разработал гибридный метод с советами ваших ребят! Так что спасибо. Недавно я заметил, что регулярные «объекты» не позволяют перехватывать обезьяны ... Интересно, почему это так? Сделал бы их намного более полезными ИМО. – mpen

1

В качестве другой альтернативы, вы можете сделать функции в связанные методы класса следующим образом:

class _FooImpl(object): 
    a = "Hello " 
    @classmethod 
    def foo(cls, param): 
     return cls.a + param 
foo = _FooImpl.foo 

# later... 
print foo("World") # yes, Hello World 

# and if you have to change an attribute: 
foo.im_self.a = "Goodbye " 

Если вы хотите функции разделить атрибут namespaecs, вы просто сделать их частью одного и того же класса. Если нет, дайте каждому свой класс.