Python 中的变量名自省

发布于 2024-08-14 11:09:28 字数 561 浏览 6 评论 0原文

Python 中是否可以动态确定变量的名称?

例如,我有时会遇到以下情况:

name = foo if bar else baz
type = alpha or bravo

D = {
    "name": name,
    "type": type
}

如果可以使用诸如 D = makedict(name, type) 之类的东西来减少重复,那就太好了。

与此相关的是,有时函数知道自己的名称会很有帮助:

class Item(object):

    def item_create(self, item):
        dispatch("create", item)

    def item_delete(self, item):
        dispatch("delete", item)

这里可以通过传递诸如 __methodname__ 之类的内容来减少重复,而不是分别显式重复“create”和“delete”。 (我认为可以使用装饰器来实现此目的,但这似乎有点矫枉过正。)

Is it possible to dynamically determine the name of a variable in Python?

For example, I sometimes have the following situation:

name = foo if bar else baz
type = alpha or bravo

D = {
    "name": name,
    "type": type
}

It would be nice if duplication there could be reduced with something like D = makedict(name, type).

Somewhat relatedly, it would sometimes be helpful for a function to know its own name:

class Item(object):

    def item_create(self, item):
        dispatch("create", item)

    def item_delete(self, item):
        dispatch("delete", item)

Here duplication might be reduced by passing something like __methodname__ instead of explicitly repeating "create" and "delete", respectively.
(I assume a decorator could be used for this, but that seems like overkill.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

横笛休吹塞上声 2024-08-21 11:09:28

在一般情况下,您无法从值中推断出名称(可能没有名称,可能有多个名称,等等);当你调用假设的 makedict(name) 时,name就是 makedict 接收到的,所以(再次,在一般情况下)它无法辨别该值来自哪个名称(如果有)。您可以内省调用者的命名空间,看看您是否足够幸运,能够遇到一种特殊情况,即该值确实可以让您推断名称(例如,您收到 23,并且整个命名空间中只有一个名称感兴趣的值恰好是 23!),但这显然是一个脆弱且不确定的架构。另外,在您的第一个示例中,绝对保证不会发生特殊情况 - name 中的值与 foo< 中的值完全相同/code> 或 baz,因此 100% 确定该值的名称将非常不明确。

您可以采取完全不同的策略,例如调用 makedict('name type', locals()) (显式传递 locals() 可能会通过黑暗和深入的内省来避免魔法,但这不是一般情况下最可靠的选择)——传入名称(我建议还有命名空间!)并让makedict推导出值< /strong>,这显然是一个更可靠的命题(因为每个名称都有一个值,但反之亦然)。即:

def makedict(names, *namespaces):
  d = {}
  for n in names.split():
    for ns in namespaces:
      if n in ns:
        d[n] = ns[n]
        break
    else:
      d[n] = None  # or, raise an exception

如果您热衷于通过内省挖掘名称空间,而不是让调用者明确指定它们,请查看 inspect.getouterframes - 但我建议你重新考虑。

您提出的第二个问题完全不同(尽管您可以再次使用 inspect 函数来内省调用者的名称或函数自己的名称 - 多么奇怪的想法!)。这两种情况的共同点是,您正在使用极其强大且危险的机器来完成一项可以更简单地完成的工作(更容易确保正确性,更容易调试任何问题,更容易测试等等) - - 装饰器远非“矫枉过正”,它们比您提出的内省更简单、更明确。如果您有无数种形式的方法:

def item_blah(self, item):
    dispatch("blah", item)

创建它们的最简单方法可能是:

class Item(object): pass

def _makedispcall(n):
  def item_whatever(self, item):
    dispatch(n, item)
  item_whatever.__name__ = 'item_' + n
  return item_whatever

for n in 'create delete blah but wait theres more'.split():
  setattr(Item, 'item_' + n, _makedispcall(n))

避免重复是一个好主意,但运行时自省通常不是实现该想法的最佳方法,并且 Python 提供了许多这种实现的替代方法。

In the general case, you cannot deduce the name from a value (there might be no name, there might be multiple ones, etc); when you call your hypothetical makedict(name), the value of name is what makedict receives, so (again, in the general case) it cannot discern what name (if any) the value came from. You could introspect your caller's namespaces to see if you're lucky enough to hit a special case where the value does let you infer the name (e.g., you receive 23, and there's only one name throughout the namespaces of interest which happens to have a value of 23!), but that's clearly a fragile and iffy architecture. Plus, in your first example case, it's absolutely guaranteed that the special case will not occur -- the value in name is exactly the same as in either foo or baz, so it's 100% certain that the name for that value will be hopelessly ambiguous.

You could take a completely different tack, such as calling makedict('name type', locals()) (passing locals() explicitly might be obviated with dark and deep introspection magic, but that's not the most solid choice in general) -- pass in the names (and namespaces, I suggest!) and have makedict deduce the values, which is obviously a much more solid proposition (since each name has exactly one value, but not viceversa). I.e.:

def makedict(names, *namespaces):
  d = {}
  for n in names.split():
    for ns in namespaces:
      if n in ns:
        d[n] = ns[n]
        break
    else:
      d[n] = None  # or, raise an exception

If you're keen on digging out the namespaces by introspection, rather than have them cleanly specified by the caller, look at inspect.getouterframes -- but I suggest you reconsider.

The second issue you raise is quite different (though you could use inspect functions again to introspect the caller's name, or a function's own name -- what a peculiar idea!). What's in common in the two cases is that you're using extremely powerful and dangerous machinery to do a job that could be done much more simply (easier to ensure correctness, easier to debug any problems, easier to test, etc, etc) -- far from having decorators be "overkill", they're far simpler and more explicit than the introspection you propose. If you have a zillion methods all of the form:

def item_blah(self, item):
    dispatch("blah", item)

the simplest way to create them might be:

class Item(object): pass

def _makedispcall(n):
  def item_whatever(self, item):
    dispatch(n, item)
  item_whatever.__name__ = 'item_' + n
  return item_whatever

for n in 'create delete blah but wait theres more'.split():
  setattr(Item, 'item_' + n, _makedispcall(n))

Avoiding repetition is an excellent idea, but runtime introspection is not generally the best way to implement that idea, and Python offers many alternative ways to such implementation.

流绪微梦 2024-08-21 11:09:28

一般来说,在Python中你不能这样做,因为Python有对象,没有变量。

如果我有

L1 = [1,2]
L2 = L1

那么,L1L2 都引用同一个对象。它没有单一名称。

同样:

def f1(): pass
f2 = f1

现在该函数没有单一名称,因此您无法找到该函数的“the”名称。

Python 中的对象可以由多个名称引用 - 当对象的引用计数为零时,Python 会为其释放内存。

In general, you can't do this in Python, because Python has objects, not variables.

If I have

L1 = [1,2]
L2 = L1

Then, L1 and L2 both refer to the same object. It has no single name.

Similarly:

def f1(): pass
f2 = f1

Now the function has no single name, and as such, you can't find out "the" name of the function.

An object in Python can be referenced by many names - when the reference count of an object goes to zero, Python frees the memory for it.

枫林﹌晚霞¤ 2024-08-21 11:09:28

您可以对函数执行您想要的操作:

>>> def blagnorf():
    pass

>>> print blagnorf.__name__
blagnorf

但不幸的是,不能对变量执行您想要的操作。不过,您可能可以为您的 python 代码编写一个预处理器来为您完成此操作...

请注意,您可以在Scheme/Lisp 中使用它们那里的宏系统来完成此操作。

You can do what you want for functions:

>>> def blagnorf():
    pass

>>> print blagnorf.__name__
blagnorf

But not for variables, unfortunately. You could probably write a preprocessor for your python code to do it for you, though...

Note that you can do this in Scheme/Lisp with the macro system they have there.

巡山小妖精 2024-08-21 11:09:28

每当您渴望创建无数个简单方法(其内部结构仅根据方法名称而有所不同)时,您可能应该重新考虑您的接口。相反,您可以这样做:

class Item(object):
    def item_action(self, action, item):
        dispatch(action, item)

其中操作可以是“创建”、“删除”等。

Whenever you get a hankering to make a gazillion simple methods whose internals only differ according to the method's name, you probably should reconsider your interface. Instead, you could do this:

class Item(object):
    def item_action(self, action, item):
        dispatch(action, item)

where action could be "create", "delete", etc.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文