【问题标题】:Getting an instance name inside class __init__() [duplicate]在类 __init__() 中获取实例名称
【发布时间】:2009-11-06 20:58:57
【问题描述】:

在 python 中构建一个新的类对象时,我希望能够根据类的实例名称创建一个默认值,而无需传入额外的参数。我怎样才能做到这一点?这是我正在尝试的基本伪代码:

class SomeObject():
    defined_name = u""

    def __init__(self, def_name=None):
        if def_name == None:
            def_name = u"%s" % (<INSTANCE NAME>)
        self.defined_name = def_name

ThisObject = SomeObject()
print ThisObject.defined_name   # Should print "ThisObject"

【问题讨论】:

  • 在我的内部网络的 irc 脚本中管理频道信息。
  • 这很奇怪。什么语言允许在运行时对编译时变量进行这种引用?
  • 在此处查看我的原始帖子:stackoverflow.com/a/59364138/5088165

标签: python class variables object instance


【解决方案1】:

嗯,几乎有办法做到这一点:

#!/usr/bin/env python
import traceback
class SomeObject():
    def __init__(self, def_name=None):
        if def_name == None:
            (filename,line_number,function_name,text)=traceback.extract_stack()[-2]
            def_name = text[:text.find('=')].strip()
        self.defined_name = def_name

ThisObject = SomeObject()
print ThisObject.defined_name 
# ThisObject

traceback 模块允许您查看用于调用 SomeObject() 的代码。 稍微扯点字符串,text[:text.find('=')].strip() 你可以 猜猜 def_name 应该是什么。

但是,这个 hack 很脆弱。例如,这不太好用:

ThisObject,ThatObject = SomeObject(),SomeObject()
print ThisObject.defined_name
# ThisObject,ThatObject
print ThatObject.defined_name 
# ThisObject,ThatObject

所以如果你要使用这个 hack,你必须记住你必须调用 SomeObject() 使用简单的python语句:

ThisObject = SomeObject()

顺便说一下,作为使用回溯的另一个例子,如果你定义了

def pv(var):
    # stack is a list of 4-tuples: (filename, line number, function name, text)
    # see http://docs.python.org/library/traceback.html#module-traceback
    #
    (filename,line_number,function_name,text)=traceback.extract_stack()[-2]
    # ('x_traceback.py', 18, 'f', 'print_var(y)')
    print('%s: %s'%(text[text.find('(')+1:-1],var))

那你就可以打电话了

x=3.14
pv(x)
# x: 3.14

打印变量名和它的值。

【讨论】:

  • 哇,这太迂回了,它几乎在喊“你不应该这样做!” :)
  • 首先它被称为hack,然后是roundabout,...很快它就会被称为成语:)
【解决方案2】:

实例没有名称。当全局名称ThisObject绑定到通过评估SomeObject 构造函数创建的实例时,构造函数已经完成运行。

如果你想让一个对象有一个名字,只需在构造函数中传递这个名字。

def __init__(self, name):
    self.name = name

【讨论】:

  • 最喜欢这个答案,因为我已经知道 Python 中没有真正的变量(确实应该有一种方法来获取绑定到实例的名称,但这是另一个参数我猜另一天)。它不会将名称绑定到实例是合乎逻辑的,所以我想我暂时只需要 'def_name' 而不是 'def_name=None'
  • 根据您的定义,没有“变量”之类的东西。在哪种语言中,某个对象只能绑定一个名称?
  • @AkoiMeexx 说真的,根据您的定义,哪种语言具有“真实”变量?
  • @JonathanFeinberg 我的预感是 OP 是 Matlab 本地人。这种事情在 Matlab 中真的很容易......它在(计算机)内存上很糟糕,对程序员来说很容易。
【解决方案3】:

您可以在类中创建一个方法来检查当前帧中的所有变量并使用hash() 查找self 变量。

这里提出的解决方案将返回所有指向实例对象的变量。

在下面的类中,isinstance() 用于避免在应用 hash() 时出现问题,因为例如 numpy.arraylist 等一些对象是不可散列的。

import inspect
class A(object):
    def get_my_name(self):
        ans = []
        frame = inspect.currentframe().f_back
        tmp = dict(frame.f_globals.items() + frame.f_locals.items())
        for k, var in tmp.items():
            if isinstance(var, self.__class__):
                if hash(self) == hash(var):
                    ans.append(k)
        return ans

已完成以下测试:

def test():
    a = A()
    b = a
    c = b
    print c.get_my_name()

结果是:

test()
#['a', 'c', 'b']

【讨论】:

    【解决方案4】:

    这行不通,想象一下:a = b = TheMagicObjet()。名称对值没有影响,它们只是指向它们。

    【讨论】:

    • 对,但事实是我不想要值本身,而是指向它们的名称作为字符串。
    • 事实是,在 THC4k 的示例中,TheMagicObjet() 有两个名称指向它,'a' 和 'b' - 你想要哪一个?
    • 此列表中的对象的名称是什么:L = [TheMagicObjet() for x in xrange(10)]?
    【解决方案5】:

    实现这一目标的一种可怕、可怕的方法是颠倒责任:

    class SomeObject():
        def __init__(self, def_name):
            self.defined_name = def_name
            globals()[def_name] = self
    
    SomeObject("ThisObject")
    print ThisObject.defined_name
    

    如果你想支持全局范围以外的东西,你必须做一些更糟糕的事情。

    【讨论】:

      【解决方案6】:

      在 Python 中,所有数据都存储在对象中。此外,名称可以与对象绑定,之后可以使用该名称查找该对象。

      对象可能绑定的名称(如果有的话)没有区别。它可能绑定到几十个不同的名称,或者没有。此外,Python 没有任何从对象指向名称的“反向链接”。

      考虑这个例子:

      foo = 1
      bar = foo
      baz = foo
      

      现在,假设您有一个值为 1 的整数对象,并且您想向后工作并找到它的名称。你会打印什么?三个不同的名称都绑定了该对象,并且都同样有效。

      print(bar is foo) # prints True
      print(baz is foo) # prints True
      

      在 Python 中,名称是访问对象的一种方式,因此无法直接使用名称。您可以搜索各种名称空间,直到找到与感兴趣的对象绑定的名称,但我不建议这样做。

      How do I get the string representation of a variable in python?

      有一个名为“像 Pythonista 一样编写代码”的著名演讲将这种情况总结为“其他语言有‘变量’”和“Python 有‘名字’”

      http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html#other-languages-have-variables

      【讨论】:

      • 对不起,python在什么意义上没有变量?
      • @Marcin,您是否觉得我的解释难以理解?我会稍微重写一下,也许你以后会更好地理解它。
      • (1) 你没有解释python缺少变量的说法。 (2) 这是不正确的。所有使用盒装类型的语言都有像 python 这样的变量(除了它们的变量可以被输入)。这只是试图为不存在的python声明一个区别。
      • @Marcin,如果你真的想理解我的原始陈述,请阅读 Ben Goodger 演示文稿(幻灯片名为“其他语言有‘变量’”和下面的幻灯片名为“Python 有‘名字’ ”)。然后考虑表达式a = b;在类 C 语言中,这意味着 value复制,但在 Python 中,这意味着 namere-bound。这就是我试图解释的区别。很抱歉您觉得我的解释难以理解。
      • 我已阅读演示文稿。我明白。它仍然不正确。在python中,指向对象的指针被复制到变量中。这也是 python 是按值调用的原因。再一次,这不是 python 独有的。
      【解决方案7】:

      受 unutbu 和 Saullo Castro 答案的启发,我创建了一个更复杂的类,甚至可以被子类化。它解决了问题中的要求。

      "根据类的实例名创建一个默认值 无需传入额外的参数。”

      当创建此类或子类的实例时,它的作用如下:

      1. 在帧堆栈中上移,直到第一帧不属于当前实例的方法。
      2. 检查此框架以获取属性self.creation_(name/file/module/function/line/text)
      3. 额外检查一个名为 self.creation_name 的对象是否实际定义在框架的 locals() 命名空间中,以确保 100% 确保找到的 creation_name 是正确的,否则会引发错误。

      代码:

      import traceback, threading, time
      
      class InstanceCreationError(Exception):
          pass
      
      class RememberInstanceCreationInfo:
          def __init__(self):
              for frame, line in traceback.walk_stack(None):
                  varnames = frame.f_code.co_varnames
                  if varnames is ():
                      break
                  if frame.f_locals[varnames[0]] not in (self, self.__class__):
                      break
                      # if the frame is inside a method of this instance,
                      # the first argument usually contains either the instance or
                      #  its class
                      # we want to find the first frame, where this is not the case
              else:
                  raise InstanceCreationError("No suitable outer frame found.")
              self._outer_frame = frame
              self.creation_module = frame.f_globals["__name__"]
              self.creation_file, self.creation_line, self.creation_function, \
                  self.creation_text = \
                  traceback.extract_stack(frame, 1)[0]
              self.creation_name = self.creation_text.split("=")[0].strip()
              super().__init__()
              threading.Thread(target=self._check_existence_after_creation).start()
      
          def _check_existence_after_creation(self):
              while self._outer_frame.f_lineno == self.creation_line:
                  time.sleep(0.01)
              # this is executed as soon as the line number changes
              # now we can be sure the instance was actually created
              error = InstanceCreationError(
                      "\nCreation name not found in creation frame.\ncreation_file: "
                      "%s \ncreation_line: %s \ncreation_text: %s\ncreation_name ("
                      "might be wrong): %s" % (
                          self.creation_file, self.creation_line, self.creation_text,
                          self.creation_name))
              nameparts = self.creation_name.split(".")
              try:
                  var = self._outer_frame.f_locals[nameparts[0]]
              except KeyError:
                  raise error
              finally:
                  del self._outer_frame
              # make sure we have no permament inter frame reference
              # which could hinder garbage collection
              try:
                  for name in nameparts[1:]: var = getattr(var, name)
              except AttributeError:
                  raise error
              if var is not self: raise error
      
          def __repr__(self):
              return super().__repr__()[
                     :-1] + " with creation_name '%s'>" % self.creation_name
      

      一个简单的例子:

      class MySubclass(RememberInstanceCreationInfo):
          def __init__(self):
              super().__init__()
      
          def print_creation_info(self):
              print(self.creation_name, self.creation_module, self.creation_function,
                      self.creation_line, self.creation_text, sep=", ")
      
      instance = MySubclass()
      instance.print_creation_info()
      #out: instance, __main__, <module>, 68, instance = MySubclass()
      

      如果无法正确确定创建名称,则会引发错误:

      variable, another_instance = 2, MySubclass()
      
      # InstanceCreationError: 
      # Creation name not found in creation frame.
      # creation_file: /.../myfile.py 
      # creation_line: 71 
      # creation_text: variable, another_instance = 2, MySubclass()
      # creation_name (might be wrong): variable, another_instance
      

      【讨论】:

        【解决方案8】:

        如果名称是指向任何对象的指针,我认为它们很重要。 没关系:

        foo = 1
        bar = foo
        

        我知道 foo 指向 1 和 bar 指向相同的值 1 进入相同的内存空间。 但假设我想创建一个类,其中包含一个向其添加对象的函数。

        Class Bag(object):
           def __init__(self):
               some code here...
           def addItem(self,item):
               self.__dict__[somewaytogetItemName] = item
        

        所以,当我像下面这样实例化类包时:

        newObj1 = Bag()
        newObj2 = Bag()
        newObj1.addItem(newObj2)I can do this to get an attribute of newObj1:
        newObj1.newObj2
        

        【讨论】:

          【解决方案9】:

          如果您想要一个类的唯一实例名称,请尝试__repr__()id(self)

          class Some:
              def __init__(self):
                  print(self.__repr__())  # = hex(id(self))
                  print(id(self))
          

          它将打印实例的内存地址,这是唯一的。

          【讨论】:

            【解决方案10】:

            最好的方法实际上是将名称传递给构造函数,就像选择的答案一样。但是,如果您真的不想让用户将名称传递给构造函数,则可以执行以下 hack

            如果您在命令行中使用 'ThisObject = SomeObject()' 创建实例,则可以从命令历史记录中的命令字符串中获取对象名称:

            import readline
            import re
            
            class SomeObject():
                def __init__(self):
                    cmd = readline.get_history_item(readline.get_current_history_length())                                                          
                    self.name = re.split('=| ',cmd)[0]
            

            如果您使用 'exec' 命令创建实例,您可以通过以下方式处理:

            if cmd[0:4] == 'exec': self.name = re.split('\'|=| ',cmd)[1]     # if command performed using 'exec'
            else: self.name = re.split('=| ',cmd)[0]
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2011-01-14
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-06-06
              相关资源
              最近更新 更多