24

While building a new class object in python, I want to be able to create a default value based on the instance name of the class without passing in an extra argument. How can I accomplish this? Here's the basic pseudo-code I'm trying for:

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"
Jonathan Feinberg
  • 44,698
  • 7
  • 80
  • 103
Akoi Meexx
  • 767
  • 2
  • 6
  • 15

10 Answers10

29

Well, there is almost a way to do it:

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

The traceback module allows you to peek at the code used to call SomeObject(). With a little string wrangling, text[:text.find('=')].strip() you can guess what the def_name should be.

However, this hack is brittle. For example, this doesn't work so well:

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

So if you were to use this hack, you have to bear in mind that you must call SomeObject() using simple python statement:

ThisObject = SomeObject()

By the way, as a further example of using traceback, if you define

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

then you can call

x=3.14
pv(x)
# x: 3.14

to print both the variable name and its value.

unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
24

Instances don't have names. By the time the global name ThisObject gets bound to the instance created by evaluating the SomeObject constructor, the constructor has finished running.

If you want an object to have a name, just pass the name along in the constructor.

def __init__(self, name):
    self.name = name
Jonathan Feinberg
  • 44,698
  • 7
  • 80
  • 103
  • Liked this answer best, since I'm already aware that there aren't true variables in Python(there really should still be a way to grab the name bound to an instance, but that's another argument for another day I guess). It's logical that it wouldn't have the name bound to the instance, so I guess for the time being I'll just require 'def_name' instead of 'def_name=None' – Akoi Meexx Nov 06 '09 at 21:31
  • 4
    There is no such thing as a "variable" by your definition. In what language can some object be bound to only one name? – Jonathan Feinberg Nov 06 '09 at 21:33
  • 1
    @AkoiMeexx Seriously, what language has "true" variables by your definition? – Marcin Jun 19 '13 at 20:12
  • @JonathanFeinberg My hunch is OP is a Matlab native. This kind of thing is really easy in Matlab...it is awful on (computer) memory, easy on programmer. – eric Oct 24 '17 at 13:40
11

You can create a method inside your class that check all variables in the current frame and use hash() to look for the self variable.

The solution proposed here will return all the variables pointing to the instance object.

In the class below, isinstance() is used to avoid problems when applying hash(), since some objects like a numpy.array or a list, for example, are unhashable.

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

The following test has been done:

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

The result is:

test()
#['a', 'c', 'b']
Saullo G. P. Castro
  • 56,802
  • 26
  • 179
  • 234
4

This cannot work, just imagine this: a = b = TheMagicObjet(). Names have no effect on Values, they just point to them.

Jochen Ritzel
  • 104,512
  • 31
  • 200
  • 194
  • 2
    Right, but the fact of the matter is that I don't want the values themselves, but the name pointing to them as a string. – Akoi Meexx Nov 06 '09 at 21:35
  • 3
    The fact of the matter is that in THC4k's example, TheMagicObjet() has 2 names pointing to it, 'a' and 'b' - which one do you want? – PaulMcG Nov 06 '09 at 22:04
  • 3
    What are the names of the objects in this list: `L = [TheMagicObjet() for x in xrange(10)]` ? – u0b34a0f6ae Nov 07 '09 at 00:58
2

In Python, all data is stored in objects. Additionally, a name can be bound with an object, after which that name can be used to look up that object.

It makes no difference to the object what names, if any, it might be bound to. It might be bound to dozens of different names, or none. Also, Python does not have any "back links" that point from an object to a name.

Consider this example:

foo = 1
bar = foo
baz = foo

Now, suppose you have the integer object with value 1, and you want to work backwards and find its name. What would you print? Three different names have that object bound to them, and all are equally valid.

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

In Python, a name is a way to access an object, so there is no way to work with names directly. You could search through various name spaces until you find a name that is bound with the object of interest, but I don't recommend this.

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

There is a famous presentation called "Code Like a Pythonista" that summarizes this situation as "Other languages have 'variables'" and "Python has 'names'"

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

Community
  • 1
  • 1
steveha
  • 74,789
  • 21
  • 92
  • 117
  • I'm sorry, but in what sense does python not have variables? – Marcin Jun 19 '13 at 20:13
  • @Marcin, did you find my explanation difficult to understand? I will rewrite it a bit and perhaps you will understand it better afterward. – steveha Jun 20 '13 at 05:20
  • (1) You do not explain the statement that python lacks variables. (2) This is incorrect. All languages that use boxed types have variables like python (except that their variables may be typed). This is simply an attempt to claim a distinction for python which does not exist. – Marcin Jun 20 '13 at 11:37
  • @Marcin, if you genuinely want to understand my original statement, please read the Ben Goodger presentation (the slide called "Other languages have 'variables'" and the following slide called "Python has 'names'"). Then consider the expression `a = b`; in a C-like language, this means a *value* is *copied*, but in Python it means a *name* is *re-bound*. That is the distinction I was trying to explain. I apologize that you found my explanation difficult to understand. – steveha Jun 20 '13 at 20:13
  • I've read the presentation. I understand it. It's still incorrect. In python, the pointer to the object is copied to the variable. This is also why python is call-by-value. Once again, this is not unique to python. – Marcin Jun 20 '13 at 20:17
  • @Marcin, this will be my last comment in this discussion. (If we keep going back-and-forth, StackOverflow will chide us for not moving it to the discussion page.) If I understand correctly, your position is that Python names should actually be considered variables that can only be assigned references to Python objects. While this is literally true, the official documentation for Python refers to "names" having values "bound" to them, and exception names follow this model as well: http://docs.python.org/3/reference/executionmodel.html – steveha Jun 21 '13 at 06:05
  • It really doesn't matter what the language standard calls them. That's a good way to describe variables, which avoids ambiguity. It doesn't mean they're not variables. It really just seems to me that you and the presentation writer are not familiar with many programming languages. If you were, you would not claim this as something special. – Marcin Jun 21 '13 at 14:03
  • @Marcin, Perhaps [this distinction may help](http://en.wikipedia.org/wiki/Variable_%28computer_science%29): In Python and some other languages, a `label` is merely a lookup name, and can be destroyed at any time without necessarily destroying the object it refers to, or even assigned to another object without affecting the first object it was assigned to. [In some other languages](http://en.wikibooks.org/wiki/C_Programming/Variables), once defined, a `variable` **IS** the object, permanently bound to it, [and that name may not be reused](http://stackoverflow.com/a/4371938/902825). – pythonlarry Oct 06 '13 at 16:42
  • @PythonLarry It's true that some languages have maths-type variables; but that doesn't mean that languages that don't, don't have variables. "Variable" is the accepted term for mutable variables, despite your dislike of it. – Marcin Oct 06 '13 at 17:54
  • @Marcin, [I don't "dislike" the term `variable`](http://rationalwiki.org/wiki/Don%27t_feed_the_Troll), I was just pointing to information related to what I think some are trying to say: not that Python is, necessarily, **better** nor _**unique**_, merely that there is a potential _gotcha_ if one looks at `labels` from the wrong point of view - it is a _label_, a _reference_, not _**THE**_ object. There _are_ tragically some Python advocates that sneer at "lesser" languages, but I find most are simply trying to help others know the differences _so they are not bitten by incorrect assumptions_. – pythonlarry Oct 09 '13 at 13:55
2

One horrible, horrible way to accomplish this is to reverse the responsibilities:

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

SomeObject("ThisObject")
print ThisObject.defined_name

If you wanted to support something other than global scope, you'd have to do something even more awful.

Russell Borogove
  • 18,516
  • 4
  • 43
  • 50
1

If you want an unique instance name for a class, try __repr__() or id(self)

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

It will print the memory address of the instance, which is unique.

Guillaume Lebreton
  • 2,586
  • 16
  • 25
1

Inspired by the answers of unutbu and Saullo Castro, I have created a more sophisticated class that can even be subclassed. It solves what was asked for in the question.

"create a default value based on the instance name of the class without passing in an extra argument."

Here's what it does, when an instance of this class or a subclass is created:

  1. Go up in the frame stack until the first frame which does not belong to a method of the current instance.
  2. Inspect this frame to get the attributes self.creation_(name/file/module/function/line/text).
  3. Perform an an additional check whether an object with name self.creation_name was actually defined in the frame's locals() namespace to make 100% sure the found creation_name is correct or raise an error otherwise.

The Code:

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

A simple example:

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

If the creation name cannot be determined properly an error is raised:

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
TheoRet
  • 31
  • 4
0

I think that names matters if they are the pointers to any object.. no matters if:

foo = 1
bar = foo

I know that foo points to 1 and bar points to the same value 1 into the same memory space. but supose that I want to create a class with a function that adds a object to it.

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

So, when I instantiate the class bag like below:

newObj1 = Bag()
newObj2 = Bag()
newObj1.addItem(newObj2)I can do this to get an attribute of newObj1:
newObj1.newObj2
andrexterz
  • 17
  • 3
-1

The best way is really to pass the name to the constructor as in the chosen answer. However, if you REALLY want to avoid asking the user to pass the name to the constructor, you can do the following hack:

If you are creating the instance with 'ThisObject = SomeObject()' from the command line, you can get the object name from the command string in command history:

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]

If you are creating the instance using 'exec' command, you can handle this with:

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