简体   繁体   English

函数如何访问自己的属性?

[英]How can a function access its own attributes?

is it possible to access the python function object attributes from within the function scope?是否可以从函数范围内访问 python 函数对象属性?

eg let's have例如让我们有

def f():
    return SOMETHING

f._x = "foo"
f()           # -> "foo"

now, what SOMETHING has to be, if we want to have the _x attribute content "foo" returned?现在,如果我们想要返回 _x 属性内容“foo”,必须是什么? if it's even possible (simply)如果它甚至可能(简单地)

thanks谢谢

UPDATE:更新:

i'd like the following work also:我还想要以下工作:

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

UPDATE 2:更新 2:

Statement that it is not possible (if it is the case), and why, is more satisfying than providing a way how to fake it eg with a different object than a function声明这是不可能的(如果是这种情况)以及为什么,比提供一种如何伪造它的方法更令人满意,例如使用与函数不同的对象

Solution解决方案

Make one of the function's default arguments be a reference to the function itself.使函数的默认参数之一成为对函数本身的引用。

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

Example usage:用法示例:

>>> f.x = 17
>>> b = f
>>> del f
>>> b()
17

Explanation解释

The original poster wanted a solution that does not require a global name lookup.最初的海报想要一个不需要全局名称查找的解决方案。 The simple solution简单的解决方案

def f():
    return f.x

performs a lookup of the global variable f on each call, which does not meet the requirements.每次调用都查找全局变量f ,不符合要求。 If f is deleted, then the function fails.如果f被删除,则函数失败。 The more complicated inspect proposal fails in the same way.更复杂的inspect提议以同样的方式失败。

What we want is to perform early binding and store the bound reference within the object itself.我们想要的是执行早期绑定并将绑定的引用存储在对象本身中。 The following is conceptually what we are doing:以下是我们在概念上所做的:

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

In the above, self is a local variable, so no global lookup is performed.在上面, self是一个局部变量,所以没有执行全局查找。 However, we can't write the code as-is, because f is not yet defined when we try to bind the default value of self to it.但是,我们不能按原样编写代码,因为当我们尝试将self的默认值绑定到f时还没有定义它。 Instead, we set the default value after f is defined.相反,我们在定义f之后设置默认值。

Decorator装饰器

Here's a simple decorator to do this for you.这是一个简单的装饰器,可以为您完成此操作。 Note that the self argument must come last, unlike methods, where self comes first.请注意, self参数必须放在最后,与方法不同, self放在最前面。 This also means that you must give a default value if any of your other arguments take a default value.这也意味着如果您的任何其他参数采用默认值,您必须提供默认值。

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)

Example:例子:

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

You could just use a class to do this你可以只使用一个类来做到这一点

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

Well, let's look at what function is:好吧,让我们看看什么是函数:

>>> 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

Aha!啊哈! So the attribute was added to the function object but it won't see it because it is looking for global x instead.所以该属性被添加到函数对象中,但它不会看到它,因为它正在寻找全局x

We can try to grab the frame of the function execution and try to look what's there (essentially what Anthony Kong suggested but w/o inspect module):我们可以尝试抓取函数执行的框架并尝试查看那里有什么(基本上是 Anthony Kong 建议的,但没有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'

Aha!啊哈! So maybe we can get the name of the function from the name of the code block and then look in round-about way for the attribute?那么也许我们可以从代码块的名称中获取函数的名称,然后再绕行查找属性? Sure enough:果然:

>>> 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

That's great!那太棒了! But would it stand the renaming and deletion of original function?但它是否经得住原有功能的重命名和删除?

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

Ah, very doubtful.啊,非常怀疑。 The function object and its code object still insist they are called foo .函数对象及其代码对象仍然坚持称它们为foo Sure enough, here is where it breaks:果然,这里是它中断的地方:

>>> g.x
777
>>> g.x=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!当! So in general it can't be done through introspection via the execution frames.所以一般来说,它不能通过执行框架通过内省来完成。 The problems seems to be that there is a difference between function object and code object - code objects are what is executed and is just one attribute func_code of the function-object and as such has no access to the func_dict attribute, where our attribute x is:问题似乎是函数对象代码对象之间存在差异——代码对象是被执行的对象,它只是函数对象的一个​​属性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}

There is of course other chicanery you can do so that it seems as function - in particular the trick with class definition... but that is not a function per se.当然,您还可以使用其他技巧,使其看起来像函数——尤其是类定义的技巧……但这本身并不是函数。 It all depends on what do you really need to do with that.这一切都取决于你真正需要做什么。

As a workaround you could use a factory function to fix your scope:作为一种解决方法,您可以使用工厂函数来修复您的范围:

def factory():
    def inner():
        print inner.x
    return inner


>>> foo=factory()
>>> foo.x=11
>>> foo()
11
>>> bar = foo
>>> del foo
>>> bar()
11

I doubt this is the best way to accomplish this, but you can access the attributes by using the method's name within the method:我怀疑这是实现此目的的最佳方法,但您可以通过在方法中使用方法名称来访问属性:

>>> 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

Here's a decorator that injects current_fun into the functions globals before executing the function.这是一个装饰器,它在执行函数之前将 current_fun 注入到函数全局变量中。 It's quite the hack, but also quite effective.这是相当的黑客,但也相当有效。

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

Here's a usage example这是一个使用示例

In [41]: f.x = '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

The answer is rather simple.答案相当简单。 Just use the fact name is looked for at execution time, not compile time:只需使用在执行时查找的事实名称,而不是编译时:

def f():
    return f._x

f._x = "foo"
f()           # -> "foo"

If you want it to be totally independent of the function name, you need some frame magic.如果您希望它完全独立于函数名称,则需要一些框架魔术。 For example:例如:

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() 

This uses a bit of a hackish approach, but it's possibly the most correct so far given that it works with the g() call as well.这使用了一些黑客方法,但鉴于它也适用于g()调用,它可能是迄今为止最正确的方法。 It works because it's relying on whatever bytecode inspection is performed by the dis module, as a shortcut.它之所以有效,是因为它依赖于dis模块执行的任何字节码检查,作为一种快捷方式。

It looks more hackish than it really is partly because the dis.disassemble() call prints to stdout, so I redirect that into a StringIO.它看起来比实际上更dis.disassemble()部分原因是dis.disassemble()调用打印到 stdout,所以我将其重定向到 StringIO。 I use disassemble() for its feature of highlighting the last instruction (add a print text line in there to see how it looks) and that makes it easier to grab the previous LOAD_NAME and the variable it used.我使用disassemble()的功能是突出显示最后一条指令(在那里添加一个print text行以查看它的外观),并且可以更轻松地获取先前的LOAD_NAME及其使用的变量。

It would be possible to use a cleaner bytecode inspection library to do this without using the dis module at all, but this proves that it's possible.完全不使用dis模块可以使用更干净的字节码检查库来执行此操作,但这证明这是可能的。 This might not be the most robust approach, but then again maybe it will work in most cases.这可能不是最强大的方法,但也许它在大多数情况下都有效。 I haven't spent enough time poking into Python internals or bytecode to know whether most CALL_FUNCTION bytecodes are preceded immediately by instructions that the regex trick would pick out.我还没有花足够的时间研究 Python 内部结构或字节码,以了解大多数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()

This generates the following output:这将生成以下输出:

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

How about using a class instead of a function and abusing the __new__ method to make the class callable as a function?如何使用类而不是函数并滥用__new__方法使类可以作为函数调用? Since the __new__ method gets the class name as the first parameter, it can access all the class attributes由于__new__方法以类名作为第一个参数,所以它可以访问所有的类属性

like in

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

this works as in这就像

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

then you can do那么你可以做

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

The issue is that even if the object behaves like a function, it is not.问题是,即使对象表现得像一个函数,它也不是。 Hence IDEs fail to provide you with the signature.因此,IDE 无法为您提供签名。

Another way to accomplish this is to define the function inside another function, and have the outer function return the inner one.实现此目的的另一种方法是在另一个函数内定义该函数,并让外部函数返回内部函数。 Then the inner function can access itself via a closure.然后内部函数可以通过闭包访问自身。 Here's a simple example:这是一个简单的例子:

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

Then:然后:

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

If there is only one method needed but you want a light-weight class with shared class state plus individual instance state, you might try the closure pattern like this:如果只需要一个方法,但你想要一个具有共享类状态和单个实例状态的轻量级类,你可以尝试这样的闭包模式:

# 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')

Here is a strategy that is probably worse than the func_defaults idea, but is interesting nonetheless.这是一个可能比func_defaults想法更糟糕的策略,但仍然很有趣。 It's hacky but I can't think of anything practically wrong with it.这很hacky,但我想不出它有什么实际问题。

We can implement a function that can refer to itself as a class with a single __new__ method (the method that normally creates a new object of that class).我们可以使用单个__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

Perhaps this pattern could be useful for a logging function...也许这种模式可能对日志记录功能有用......

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)

Just define your function inside a closure:只需在闭包中定义您的函数:

def generate_f():
    def f():
        return f.x
    return f

f = generate_f()

f.x = 314
g = f

del f
print g()
# => 314

I like this alot.我非常喜欢这个。

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)

Sorry for the late reply but I just stumbled upon this.抱歉回复晚了,但我只是偶然发现了这一点。 I would have to argue that the way that “g” is asked to work is non-Pythonic.我不得不争辩说,要求“g”工作的方式是非 Pythonic 的。 Inside function, the name “f“ refers to the value of a global variable at the time the function is called.在函数内部,名称“f”指的是调用函数时全局变量的值。 Given that, consider the following:鉴于此,请考虑以下事项:

def f():
    print(f)
f, g = 42, f
g()  # prints 42
del f
g()  # raises an exception 

Hopefully, no one argues that this is incorrect behavior.希望没有人认为这是不正确的行为。 Given that fact, I cam only vote for any answer that requires the use of a different variable name (eg “self”) inside the function.鉴于这一事实,我只会投票支持需要在函数内使用不同变量名称(例如“self”)的任何答案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 NeuralNetwork类无法访问其属性 - NeuralNetwork class can't access its attributes python,字典值是对象,该对象可以从自身内部的函数访问其自己的键值吗? - python, dictionary value is object, can that object access its own key value from a function within itself? Python-函数可以看到自己的装饰器吗 - Python - Can a function see its own decorators 如果没有 Django 中的“parent.attrib”语法,子 class 如何访问其父属性? - How can a child class access its parent attributes without the “parent.attrib” syntax in Django? python类可以访问其自己的对象的属性吗? - can a python class access attribute of its own object? 如何对使用其自身输出的滞后值的函数进行矢量化? - How can I vectorize a function that uses lagged values of its own output? 属性化的 object 能否访问其对象在 python 中的其他属性? - Can an attributed object access its object's other attributes in python? 当一个类的任何属性被修改时,它如何运行某个函数? - How can a class run some function when any of its attributes are modified? 如何将 class 的实例传递到其构造函数中的属性中,并让这些属性访问初始化的属性? - How to pass an instance of a class into attributes within its constructor and have those attributes access initialized attributes? 如何访问 __init_ 函数的属性 - How to access attributes of the __init_ function
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM