简体   繁体   中英

Get the name of the instance of an object in python, when __str__ overridden?

I'm creating a simple container system, in which my objects (all children of a class called GeneralWidget ) are grouped in some containers, which are in another set of containers, and so on until all is in one Global container. I have a custom class, called GeneralContainer , in which I had to override the __str__ method to provide a describing name for my container, So I know what kind of objects or containers are stored inside of him.

I am currently writing another class called ObjectTracker in which all positions of my Objects are stored, so when a new object is created, It gives a list with its' name in it in the __init__ method to it's "parent" in my hieracy, which adds itself to the list and passes it on. At some point this list with all objects that are above the new created instance of GeneralWidget will reach the global GeneralWidget (containing all containers and widgets) , which can access the ObjectTracker -object in my main() .

This is the bachground of my problem. My ObjectTracker has got a dictionary, in which every "First Level container" is a key, and all objects inside such a container are stored in dictionarys as well. So I have many encapsulated dictionarys.

As I don't know how many levels of containers there will be, I need a dynamic syntax that is independent of the number of dictionarys I need to pass unil I get to the place in the BIG dictionary that I want. A (static) call inside my ObjectRepository class would need to look something like this:

self._OBJECTREPOSITORY[firstlevelcontainer12][secondlevel8][lastlevel4] = myNewObject

with firstlevelcontainer12 containing secondlevel8 which contains lastlevel4 in which the new object should be placed

But I know neither how the containers will be called, nor how many there will be, so I decided to use exec() and compose a string with all names in it. I will post my actual code here, the definition of ObjectTracker :

class ObjectTracker:
    def __init__(self):
        self._NAMEREPOSITORY = {}

    def addItem(self, pathAsList):
        usableList = list(reversed(pathAsList))
        string = "self._NAMEREPOSITORY"
        for thing in usableList:
            if usableList[-1] != [thing]:
                string += "[" + str(thing) + "]"
            else:
                string += "] = " + str(thing)
        print(string)
        exec(string)                

The problem is that I have overridden the __str__ method of the class GeneralContainer and GeneralWidget To gie back a describing name. This came in VERY handy at many occasions but now it has become a big problem. The code above only works if the custom name is the same as the name of the instance of the object (of course, I get why!)

The question is : Does a built-in function exist to do the following:

>>> alis = ExampoleClass()
>>> DOESTHISEXIST(alis)
'alis'

If no, how can I write a custom one without destroying my well working naming system?

Note: Since I'm not exactly sure what you want, I'll attempt provide a general solution.

First off, avoid eval/exec like the black plague. There are serious problems one encounters when using them , and there's almost always a better way. This is the way I propose below:

You seems to want a way to find a certain point a nested dictionary given a list of specific keys. This can be done quite easily using a for-loop and recursively traversing said dictionary. For example:

>>> def get_value(dictionary, keys):
        value = dictionary
        for key in keys:
            value = value[key]
        return value

>>> d = {'a': 1, 'b': {'c': 2, 'd': 3, 'e': {'f': 4, }, 'g': 5}}
>>> get_value(d, ('b', 'e', 'f'))
4
>>> 

If you need to assign to a specific part of a certain nested dictionary, this can also be done using the above code:

>>> dd = get_value(d, ('b', 'e')) # grab a dictionary object
>>> dd
{'f': 4}
>>> dd['h'] = 6
>>> # the d dictionary is changed.
>>> d
{'a': 1, 'b': {'c': 2, 'd': 3, 'e': {'f': 4, 'h': 6}, 'g': 5}}
>>> 

Below is a formalized version of the function above, with error testing and documentation (in a custom style):

NO_VALUE = object()


def traverse_mapping(mapping, keys, default=NO_VALUE):
    """
    Description
    -----------
    Given a - often nested - mapping structure and a list of keys, use the
    keys to recursively traverse the given dictionary and retrieve a certian
    keys value.

    If the function reaches a point where the mapping can no longer be
    traversed (i.e. the current value retrieved from the current mapping
    structure is its self not a mapping type) or a given key is found to
    be non-existent, a default value can be provided to return. If no
    default value is given, exceptions will be allowed to raise as normal
    (a TypeError or KeyError respectively.)

    Examples (In the form of a Python IDLE session)
    -----------------------------------------------
    >>> d = {'a': 1, 'b': {'c': 2, 'd': 3, 'e': {'f': 4, }, 'g': 5}}
    >>> traverse_mapping(d, ('b', 'e', 'f'))
    4
    >>> inner_d = traverse_mapping(d, ('b', 'e'))
    >>> inner_d
    {'f': 4}
    >>> inner_d['h'] = 6
    >>> d
    {'a': 1, 'b': {'c': 2, 'd': 3, 'e': {'f': 4, 'h': 6}, 'g': 5}}
    >>> traverse_mapping(d, ('b', 'e', 'x'))
    Traceback (most recent call last):
      File "<pyshell#14>", line 1, in <module>
        traverse_mapping(d, ('b', 'e', 'x'))
      File "C:\Users\Christian\Desktop\langtons_ant.py", line 33, in traverse_mapping
        value = value[key]
    KeyError: 'x'
    >>> traverse_mapping(d, ('b', 'e', 'x'), default=0)
    0
    >>>

    Parameters
    ----------
    - mapping : mapping
        Any map-like structure which supports key-value lookup.

    - keys : iterable
        An iterable of keys to be using in traversing the given mapping.
    """
    value = mapping
    for key in keys:
        try:
            value = value[key]
        except (TypeError, KeyError):
            if default is not NO_VALUE:
                return default
            raise
    return value

I think you might be looking for vars() .

a = 5
# prints the value of a
print(vars()['a'])
# prints all the currently defined variables
print(vars())
# this will throw an error since b is not defined
print(vars()['b'])

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM