在python中构建一个新的类对象时,我希望能够基于类的实例名创建一个默认值,而不需要传递额外的参数。我该怎么做呢?下面是我想要的基本伪代码:
1 2 3 4 5 6 7 8 9 10 | 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" |
嗯,几乎有一种方法:
1 2 3 4 5 6 7 8 9 10 11 12 | #!/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()的代码。用一个小的字符串争吵,
然而,这个黑客是脆弱的。例如,这并不是很好:
1 2 3 4 5 | ThisObject,ThatObject = SomeObject(),SomeObject() print ThisObject.defined_name # ThisObject,ThatObject print ThatObject.defined_name # ThisObject,ThatObject |
因此,如果要使用这个hack,必须记住必须调用SomeObject()使用简单的python语句:
1 | ThisObject = SomeObject() |
顺便说一下,作为使用回溯的进一步例子,如果您定义了
1 2 3 4 5 6 7 | 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)) |
然后你可以打电话
1 2 3 | x=3.14 pv(x) # x: 3.14 |
同时打印变量名及其值。
实例没有名称。当全局名
如果希望对象具有名称,只需在构造函数中传递该名称。
1 2 | def __init__(self, name): self.name = name |
您可以在类中创建一个方法来检查当前帧中的所有变量,并使用
这里提出的解决方案将返回指向实例对象的所有变量。
在下面的类中,
1 2 3 4 5 6 7 8 9 10 11 | 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 |
进行了以下测试:
1 2 3 4 5 | def test(): a = A() b = a c = b print c.get_my_name() |
其结果是:
1 2 | test() #['a', 'c', 'b'] |
这不能工作,想象一下:
实现这一目标的一个非常非常糟糕的方法就是颠倒责任:
1 2 3 4 5 6 7 | class SomeObject(): def __init__(self, def_name): self.defined_name = def_name globals()[def_name] = self SomeObject("ThisObject") print ThisObject.defined_name |
如果你想支持全球范围之外的东西,你就必须做一些更糟糕的事情。
在Python中,所有数据都存储在对象中。此外,可以将名称与对象绑定,然后可以使用该名称查找该对象。
对象的名称与它可能绑定到的名称(如果有的话)没有关系。它可能有几十个不同的名称,或者一个也没有。此外,Python没有任何指向对象到名称的"反向链接"。
考虑一下这个例子:
1 2 3 | foo = 1 bar = foo baz = foo |
现在,假设您有一个值为1的整数对象,您想向后计算并找到它的名称。你会打印什么?有三个不同的名称将该对象绑定到它们,它们都是同样有效的。
1 2 | print(bar is foo) # prints True print(baz is foo) # prints True |
在Python中,名称是访问对象的一种方式,因此无法直接使用名称。您可以搜索各种名称空间,直到找到与感兴趣的对象绑定的名称,但我不建议这样做。
如何在python中得到变量的字符串表示?
有一个著名的演讲叫做"像Pythonista一样的代码",它总结了这种情况,"其他语言有‘变量’和‘Python有‘名称’"
http://python.net/~goodger/projects/pycon/2007/idiomatic/handout.html#other-languages-have-variables
受到unutbu和Saullo Castro的答案的启发,我创建了一个更复杂的类,甚至可以子类化。它解决了问题中的问题。
"create a default value based on the instance name of the class
without passing in an extra argument."
当创建该类或子类的实例时,它的作用如下:
在帧堆栈中向上移动,直到不属于当前实例的方法的第一个帧为止。检查此框架以获得属性代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | 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( " Creation name not found in creation frame. creation_file:" "%s creation_line: %s creation_text: %s creation_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 |
一个简单的例子:
1 2 3 4 5 6 7 8 9 10 | 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() |
如果无法正确确定创建名称,则会引发错误:
1 2 3 4 5 6 7 8 | 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 |
如果您想要一个类的唯一实例名,请尝试
1 2 3 4 | class Some: def __init__(self): print(self.__repr__()) # = hex(id(self)) print(id(self)) |
它将打印实例的内存地址,这是惟一的。
我认为名字很重要,如果它们是指向任何对象的指针…没有问题,如果:
1 2 | foo = 1 bar = foo |
我知道foo指向1 bar指向相同的值1到相同的内存空间。但假设我想创建一个类,其中包含一个添加对象的函数。
1 2 3 4 5 | Class Bag(object): def __init__(self): some code here... def addItem(self,item): self.__dict__[somewaytogetItemName] = item |
所以,当我像下面这样实例化类包时:
1 2 3 4 | newObj1 = Bag() newObj2 = Bag() newObj1.addItem(newObj2)I can do this to get an attribute of newObj1: newObj1.newObj2 |
实际上,最好的方法是按照所选的答案将名称传递给构造函数。但是,如果你真的不想让用户把名字传递给构造函数,你可以这样做:
如果你在命令行中创建一个实例'ThisObject = SomeObject()',你可以从命令历史中的命令字符串中获得对象名:
1 2 3 4 5 6 7 | 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'命令创建实例,你可以用:
1 2 | if cmd[0:4] == 'exec': self.name = re.split('\'|=| ',cmd)[1] # if command performed using 'exec' else: self.name = re.split('=| ',cmd)[0] |