как функция python может получить доступ к своим собственным атрибутам?

возможно ли получить доступ к атрибутам объекта функции python из области действия функции?

например, давайте

def f(): return SOMETHING f._x = "foo" f() # -> "foo" 

теперь, что ЧТО-ТО должно быть, если мы хотим вернуть содержимое атрибута _x «foo»? если это возможно (просто)

благодаря

ОБНОВИТЬ:

я хотел бы также следующую работу:

 g = f del f g() # -> "foo" 

ОБНОВЛЕНИЕ 2:

Утверждение, что это невозможно (если это так) и почему, более удовлетворительно, чем предоставление способа подделать его, например, с помощью другого объекта, кроме функции

  • как добавить матрицу numpy в пустой массив numpy
  • django-taggit: сделать теги не требуемыми в admin
  • Генерация xml в python и lxml
  • соскабливание нескольких страниц с помощью scrapy
  • Каков самый pythonic способ перебора всех строк из нескольких файлов?
  • В python (django) как я могу остановить доступ к глобальной структуре данных словаря при его обновлении?
  • Сортировка списка на основе значений словаря в python?
  • Как изменить дату создания файла Windows из Python?
  • 15 Solutions collect form web for “как функция python может получить доступ к своим собственным атрибутам?”

    Решение

    Сделайте один из аргументов по умолчанию функции ссылкой на саму функцию.

     def f(self): return self.x f.func_defaults = (f,) 

    Пример использования:

     >>> fx = 17 >>> b = f >>> del f >>> b() 17 

    объяснение

    Оригинальный плакат хотел получить решение, которое не требует глобального поиска имени. Простое решение

     def f(): return fx 

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

    Мы хотим выполнить раннее связывание и сохранить привязку ссылки внутри самого объекта. Следующее – концептуально то, что мы делаем:

     def f(self=f): return self.x 

    В приведенном выше self является локальной переменной, поэтому глобальный поиск не выполняется. Однако мы не можем написать код as-is, потому что f еще не определено, когда мы пытаемся связать значение по умолчанию для него. Вместо этого мы устанавливаем значение по умолчанию после определения f .

    декоратор

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

     def self_reference(f): f.func_defaults = f.func_defaults[:-1] + (f,) return f @self_reference def foo(verb, adverb='swiftly', self=None): return '%s %s %s' % (self.subject, verb, adverb) 

    Пример:

     >>> foo.subject = 'Fred' >>> bar = foo >>> del foo >>> bar('runs') 'Fred runs swiftly' 

    Вы можете просто использовать класс для этого

     >>> class F(object): ... def __call__(self, *args, **kw): ... return self._x ... >>> f=F() >>> f._x = "foo" >>> f() 'foo' >>> g=f >>> del f >>> g() 'foo' 

    Ну, давайте посмотрим, какая функция:

     >>> def foo(): ... return x ... >>> foo.x = 777 >>> foo.x 777 >>> foo() Traceback (most recent call last): File "<interactive input>", line 1, in <module> File "<interactive input>", line 2, in foo NameError: global name 'x' is not defined >>> dir(foo) ['__call__', '__class__', '__delattr__', '__dict__', '__doc__', '__get__', '__getattribute__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name', 'x'] >>> getattr(foo, 'x') 777 

    Ага! Таким образом, атрибут был добавлен к объекту функции, но он не увидит его, потому что он ищет глобальный x вместо этого.

    Мы можем попытаться захватить фрейм выполнения функции и попытаться посмотреть, что там (по существу, что предложил Энтони Конгресс, но без inspect модуля):

     >>> def foo(): ... import sys ... return sys._getframe() ... >>> fr = foo() >>> dir(fr) ['__class__', '__delattr__', '__doc__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', 'f_back', 'f_builtins', 'f_code', 'f_exc_traceback', 'f_exc_type', 'f_exc_value', 'f_globals', 'f_lasti', 'f_lineno', 'f_locals', 'f_restricted', 'f_trace'] >>> fr.f_locals {'sys': <module 'sys' (built-in)>} >>> fr.f_code <code object foo at 01753020, file "<interactive input>", line 1> >>> fr.f_code.co_code 'd\x01\x00d\x00\x00k\x00\x00}\x00\x00|\x00\x00i\x01\x00\x83\x00\x00S' >>> fr.f_code.co_name 'foo' 

    Ага! Может быть, мы сможем получить имя функции от имени блока кода, а затем взглянем вокруг атрибута? Конечно же:

     >>> getattr(fr.f_globals[fr.f_code.co_name], 'x') 777 >>> fr.f_globals[fr.f_code.co_name].x 777 >>> def foo(): ... import sys ... frm = sys._getframe() ... return frm.f_globals[frm.f_code.co_name].x ... >>> foo.x=777 >>> foo() 777 

    Замечательно! Но оставит ли это переименование и удаление оригинальной функции?

     >>> g = foo >>> g.func_name 'foo' >>> g.func_code.co_name 'foo' 

    Ах, очень сомнительно. Объект функции и его объект кода все еще настаивают на том, что они называются foo . Конечно же, вот где он ломается:

     >>> gx 777 >>> gx=888 >>> foo.x 888 >>> g() 888 >>> del foo >>> g() Traceback (most recent call last): File "<interactive input>", line 1, in <module> File "<interactive input>", line 4, in foo KeyError: 'foo' 

    Dang! Таким образом, в общем случае это невозможно сделать через интроспекцию через кадры выполнения. Проблема заключается в том, что существует разница между объектом-объектом и объектами- объектами кода – это то, что выполняется, и это всего лишь один атрибут func_code объекта-функции и как таковой не имеет доступа к func_dict , где наш атрибут x равен :

     >>> g <function foo at 0x0173AE30> >>> type(g) <type 'function'> >>> g.func_code <code object foo at 017532F0, file "<interactive input>", line 1> >>> type(g.func_code) <type 'code'> >>> g.func_dict {'x': 888} 

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

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

     def factory(): def inner(): print inner.x return inner >>> foo=factory() >>> foo.x=11 >>> foo() 11 >>> bar = foo >>> del foo >>> bar() 11 

    Я сомневаюсь, что это лучший способ выполнить это, но вы можете получить доступ к атрибутам, используя имя метода в методе:

     >>> def foo(): ... print foo.x ... >>> foo() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in foo AttributeError: 'function' object has no attribute 'x' >>> foo.x = 5 >>> foo() 5 

    Ответ довольно прост. Просто используйте имя факта во время выполнения, а не время компиляции:

     def f(): return f._x f._x = "foo" f() # -> "foo" 

    Вот декоратор, который вводит current_fun в функции globals перед выполнением функции. Это довольно взломанный, но также довольно эффективный.

     from functools import wraps def introspective(f): @wraps(f) def wrapper(*args, **kwargs): exists = 'current_fun' in f.func_globals old = f.func_globals.get('current_fun',None) f.func_globals['current_fun'] = wrapper try: return f(*args, **kwargs) finally: if exists: f.func_globals['current_fun'] = old else: del f.func_globals['current_fun'] return wrapper @introspective def f(): print 'func_dict is ',current_fun.func_dict print '__dict__ is ',current_fun.__dict__ print 'x is ',current_fun.x 

    Вот пример использования

     In [41]: fx = 'x' In [42]: f() func_dict is {'x': 'x'} __dict__ is {'x': 'x'} x is x In [43]: g = f In [44]: del f In [45]: g() func_dict is {'x': 'x'} __dict__ is {'x': 'x'} x is x 

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

     def f2(): import inspect frame = inspect.currentframe() fname = frame.f_code.co_name fobj = frame.f_globals[fname] print fobj._x f2._x = 2 f2() 

    Это использует немного хакерский подход, но, возможно, это наиболее правильно, учитывая, что он работает также с вызовом g() . Он работает, потому что он полагается на то, что проверка байт-кода выполняется этим модулем как ярлык.

    Это выглядит более хакерским, чем на самом деле отчасти потому, что dis.disassemble() печатает на stdout, поэтому я перенаправляю это в StringIO. Я использую disassemble() для своей функции выделения последней инструкции (добавьте туда print text чтобы увидеть, как она выглядит), и это облегчает захват предыдущего LOAD_NAME и используемой переменной.

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

     import inspect import dis import re import sys import StringIO def f(): caller = inspect.stack()[1][0] sys.stdout = StringIO.StringIO() dis.disassemble(caller.f_code, caller.f_lasti) text = sys.stdout.getvalue() sys.stdout = sys.__stdout__ match = re.search(r'LOAD_NAME.*\((.*?)\)\s+-->', text) name = match.group(1) try: func = caller.f_locals[name] except KeyError: func = caller.f_globals[name] return func._x f._x = 'foo' print 'call f():', f() g = f del f print 'call g():', g() 

    Это генерирует следующий результат:

     call f(): foo call g(): foo 

    Как насчет использования класса вместо функции и злоупотребления методом __new__ чтобы класс вызывался как функция? Поскольку метод __new__ получает имя класса в качестве первого параметра, он может получить доступ ко всем атрибутам класса

    как в

     class f(object): def __new__(cls, x): print cls.myattribute return x 

    это работает так же, как в

     f.myattribute = "foo" f(3) foo 3 

    то вы можете сделать

     g=f f=None g(3) foo 3 

    Проблема в том, что даже если объект ведет себя как функция, это не так. Следовательно, IDE не могут предоставить вам подпись.

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

     def makeFunc(): def f(): return f._x return f 

    Затем:

     >>> f = makeFunc() >>> f._x = "foo" >>> f() 'foo' >>> g = f >>> del f >>> g() 'foo' 

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

     # closure example of light weight object having class state, # local state, and single method # This is a singleton in the sense that there is a single class # state (see Borg singleton pattern notebook) # BUT combined with local state # As long as only one method is needed, this one way to do it # If a full class singleton object is needed with multiple # methods, best look at one of the singleton patterns def LW_Object_Factory(localState): # class state - doesn't change lwof_args = (1, 2, 3) lwof_kwargs = {'a': 4, 'b': 5} # local instance - function object - unique per # instantiation sharing class state def theObj(doc, x): print doc, 'instance:' print '\tinstance class state:\n\t\targs -', \ lwof_args, ' kwargs -', lwof_kwargs print '\tinstance locals().items():' for i in locals().items(): print '\t\t', i print '\tinstance argument x:\n\t\t', '"{}"'.format(x) print '\tinstance local state theObj.foo:\n\t\t',\ '"{}"'.format(theObj.foo) print '' # setting local state from argument theObj.foo = localState return(theObj) lwo1 = LW_Object_Factory('foo in local state for first') lwo2 = LW_Object_Factory('foo in local state for second') # prove each instance is unique while sharing class state print 'lwo1 {} distinct instance from lwo2\n'\ .format(id(lwo1) <> id(lwo2) and "IS" or "IS NOT") # run them lwo1('lwo1', 'argument lwo1') lwo2('lwo2', 'argument lwo2') 

    Вот стратегия, которая, вероятно, хуже, чем идея func_defaults , но тем не менее интересна. Это взломанно, но я не могу думать о чем-то практически неправильном.

    Мы можем реализовать функцию, которая может ссылаться на себя как класс с единственным методом __new__ (метод, который обычно создает новый объект этого класса).

     class new: """Returns True the first time an argument is passed, else False.""" seen = set() def __new__(cls, x): old = x in cls.seen cls.seen.add(x) return not old def main(): print(new(1)) # True print(new(2)) # True print(new(2)) # false is_new = new print(is_new(1)) # False 

    Возможно, этот шаблон может быть полезен для функции каротажа …

     class log_once: """Log a message if it has not already been logged. Args: msg: message to be logged printer: function to log the message id_: the identifier of the msg determines whether the msg has already been logged. Defaults to the msg itself. This is useful to log a condition that occurs many times in a single execution. It may be relevant that the condition was true once, but you did not need to know that it was true 10000 times, nor do you desire evidence to that effect to fill your terminal screen. """ seen = set() def __new__(cls, msg, printer=print, id_=None): id_ = id_ or msg if id_ not in cls.seen: cls.seen.add(id_) printer(id_) if __name__ == '__main__': log_once(1) log_once(1) log_once(2) 

    Просто определите свою функцию внутри закрытия:

     def generate_f(): def f(): return fx return f f = generate_f() fx = 314 g = f del f print g() # => 314 

    Мне это очень нравится.

     from functools import update_wrapper def dictAsGlobals(f): nf = type(f)(f.__code__, f.__dict__, f.__name__, f.__defaults__, f.__closure__) try: nf.__kwdefaults__ = f.__kwdefaults__ except AttributeError: pass nf.__dict__ = f.__dict__ nf.__builtins__ = f.__globals__["__builtins__"] return update_wrapper(nf, f) @dictAsGlobals def f(): global timesCalled timesCalled += 1 print(len.__doc__.split("\n")[0]) return factor0 * factor1 vars(f).update(timesCalled = 0, factor0 = 3, factor1 = 2) print(f()) print(f()) print(f.timesCalled) 
    Interesting Posts
    Python - лучший язык программирования в мире.