在类__init __()中获取实例名称

时间:2009-11-06 20:58:58

标签: python class variables object instance

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

10 个答案:

答案 0 :(得分:20)

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

#!/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

回溯模块允许您查看用于调用SomeObject()的代码。 通过一点点的争吵,你可以text[:text.find('=')].strip() 猜猜def_name应该是什么。

然而,这种黑客很脆弱。例如,这不能很好地运作:

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

打印变量名称及其值。

答案 1 :(得分:19)

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

如果您希望对象具有名称,只需在构造函数中传递名称。

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

答案 2 :(得分:6)

您可以在类中创建一个方法,检查当前帧中的所有变量,并使用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']

答案 3 :(得分:3)

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

答案 4 :(得分:2)

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

有一个名为“Code like a Pythonista”的着名演示文稿将这种情况概括为“其他语言有'变量'”和“Python有'名字'”

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

答案 5 :(得分:2)

实现这一目标的一种可怕而可怕的方法是扭转责任:

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

SomeObject("ThisObject")
print ThisObject.defined_name

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

答案 6 :(得分:0)

我认为,如果它们是指向任何对象的指针,那么名称很重要。 无论如何:

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

答案 7 :(得分:0)

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

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

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

答案 8 :(得分:0)

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

  

“根据类的实例名称创建默认值   没有传递额外的论点。“

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

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

    self.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()
    #out: instance, __main__, <module>, 68, instance = MySubclass()
    

答案 9 :(得分:-1)

最好的方法是将名称传递给构造函数,就像在所选答案中一样。但是,如果您真的想要避免要求用户将名称传递给构造函数,则可以执行以下操作 hack

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

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]

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

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