views:

133

answers:

2

(When I say "object address", I mean the string that you type in Python to access an object. For example 'life.State.step'. Most of the time, all the objects before the last dot will be packages/modules, but in some cases they can be classes or other objects.)

In my Python project I often have the need to play around with object addresses. Some tasks that I have to do:

  1. Given an object, get its address.
  2. Given an address, get the object, importing any needed modules on the way.
  3. Shorten an object's address by getting rid of redundant intermediate modules. (For example, 'life.life.State.step' may be the official address of an object, but if 'life.State.step' points at the same object, I'd want to use it instead because it's shorter.)
  4. Shorten an object's address by "rooting" a specified module. (For example, 'garlicsim_lib.simpacks.prisoner.prisoner.State.step' may be the official address of an object, but I assume that the user knows where the prisoner package is, so I'd want to use 'prisoner.prisoner.State.step' as the address.)

Is there a module/framework that handles things like that? I wrote a few utility modules to do these things, but if someone has already written a more mature module that does this, I'd prefer to use that.

One note: Please, don't try to show me a quick implementation of these things. It's more complicated than it seems, there are plenty of gotchas, and any quick-n-dirty code will probably fail for many important cases. These kind of tasks call for battle-tested code.

UPDATE: When I say "object", I mostly mean classes, modules, functions, methods, stuff like these. Sorry for not making this clear before.

A: 

For points 3 and 4, I guess that you are looking for facilities like

from life import life  # life represents life.life
from garlicsim_lib.simpacks import prisoner

However, this is not recommended, as it makes it harder for you or people who read your code to quickly know what prisoner represents (where module does it come from? you have to look at the beginning of the code to get this information).

For point 1, you can do:

from uncertainties import UFloat

print UFloat.__module__  # prints 'uncertainties'

import sys
module_of_UFloat = sys.modules[UFloat.__module__]

For point 2, given the string 'garlicsim_lib.simpacks.prisoner', you can get the object it refers to with:

obj = eval('garlicsim_lib.simpacks.prisoner')

This supposes that you have imported the module with

import garlicsim_lib  # or garlicsim_lib.simpacks

If you even want this to be automatic, you can do something along the lines of

import imp

module_name = address_string.split('.', 1)[0]
mod_info = imp.find_module(module_name)
try:
    imp.load_module(module_name, *mod_info)
finally:
    # Proper closing of the module file:
    if mod_info[0] is not None:
        mod_info[0].close()

This works only in the simplest cases (garlicsim_lib.simpacks need to be available in garlicsim_lib, for instance).

Coding things this way is, however, highly unusual.

EOL
Point 2 means: Given the string `'garlicsim_lib.simpacks.prisoner.State.step'`, get the actual object `garlicsim_lib.simpacks.prisoner.State.step`.
cool-RR
Why the down vote?
EOL
You've given me quick-n-dirty code. I already have some code that does this, so quick-n-dirty code is not helpful for me. I'm asking if anyone knows of a mature module that does this.
cool-RR
@cool-RR: Maybe, but downvoting someone who gives you an answer of that length isn't very grateful
jellybean
@jellybean: Downvoting and upvoting is for saying "an answer is bad" or "answer is good". If we'll avoid downvoting in order to not hurt people's feelings we'll just have a harder time telling the good answers from the bad. Thank you EOL for taking the time to answer my question.
cool-RR
@cool-RR: I agree. But in this case the answer isn't "bad" but "not helpful" (at most), which maps to "no vote" in my voting decision map.
jellybean
I think unhelpful answers are harmful because they clutter the question page.
cool-RR
@cool-RR: EOL's answer is only unhelpful to *you* personally, because you "already have some code that does this". The rest of the world doesn't have access to that code, but thanks to EOL's answer, the rest of the world now *does* have access to some admittedly-quick-and-dirty code for doing this stuff, which might be useful to people other than yourself. The answers here aren't just for you, but are also for other people asking the same or similar questions.
RichieHindle
@RichieHindle: This question was downvoted to -5, most of the people here are telling me some variation of "You're doing it wrong, what you want is wrong," and now you're concerned about other people who might find this question interesting? A much bigger problem is the "You're doing it wrong" attitude that many people showed here, rather than me downvoting an answer that seemingly ignored my ACTUAL QUESTION, which is "Is there a Python module for handling Python object addresses?"
cool-RR
@cool-RR: Sometimes I'll be driving along and I'll see ahead that there's someone wanting to pull out. If none of the cars in front me lets them out, I'll do it. Sometimes the driver I'm letting out is obviously frustrated at having to wait a long time, and drives speedily away without thanking me. It's him vs. the rest of the world, and I'm part of the rest of the world. EOL didn't downvote your question (I assume). Neither did he tell you you were doing it wrong. I didn't do any of that either. Please don't have a go at EOL or me for something that others did. Nor at EOL for trying to help.
RichieHindle
@RichieHindle: Thank you for your support. I assume that cool-RR might have good reasons to be wanting to do certain things, however unusual they are.
EOL
+6  A: 

Short answer: No. What you want is impossible.

The long answer is that what you think of as the "address" of an object is anything but. life.State.step is merely one of the ways to get a reference to the object at that particular time. The same "address" at a later point can give you a different object, or it could be an error. What's more, this "address" of yours depends on the context. In life.State.step, the end object depends not just on what life.State and life.State.step are, but what object the name life refers to in that namespace.

Specific answers to your requests:

  1. The end object has no way whatsoever of finding out how you referred to it, and neither has any code that you give the object to. The "address" is not a name, it's not tied to the object, it's merely an arbitrary Python expression that results in an object reference (as all expressions do.) You can only make this work, barely, with specific objects that aren't expected to move around, such as classes and modules. Even so, those objects can move around, and frequently do move around, so what you attempt is likely to break.

  2. As mentioned, the "address" depends on many things, but this part is fairly easy: __import__() and getattr() can give you these things. They will, however, be extremely fragile, especially when there's more involved than just attribute access. It can only remotely work with things that are in modules.

  3. "Shortening" the name requires examining every possible name, meaning all modules and all local names, and all attributes of them, recrusively. It would be a very slow and time-consuming process, and extremely fragile in the face of anything with a __getattr__ or __getattribute__ method, or with properties that do more than return a value.

  4. is the same thing as 3.

Thomas Wouters
"The same "address" at a later point can give you a different object, or it could be an error." -- Usually the kind of objects that you use addresses for are classes, functions, modules etc., and they rarely change during runtime, so we can ignore these edge cases where it does change.
cool-RR
I'm sorry I neglected to say in my question that these are the kinds of objects I mean. I updated the question, thanks.
cool-RR
""Shortening" the name requires examining every possible name" -- Okay, so you haven't thought it through. It's possible without examining every possible name, and I have a function that does this.
cool-RR
It really doesn't change the answer. You may wish to ignore these issues, but they are real issues, and real-world code will run into them. You can make part of what you want with `__import__()` and `getattr()`, as I mention, but there's nothing ready to use and there will be an absurd number of fragile corner cases.
Thomas Wouters
Thomas: Even the Python standard library ignores these edge cases. For example, if you pickle a class, it gets pickled by its address, and the edge case of the address changing is simply ignored.
cool-RR
No, a class gets pickled by *its name and the name of the module it was defined in*, not this misguided idea of 'address' you have. This is why nested classes can't be pickled. This is also why pickle is really crappy for any kind of evolving code.
Thomas Wouters
I am aware that this idea of "address" is fragile and will break for many cases. I am interesting in using it in situations where these cases are unlikely to happen. So this idea of "address" is useful to me. If you think this idea is misguided, then you are not the target audience of this idea, and you should spend your time doing something else rather than arguing with me and telling me that it's a misguided idea.
cool-RR