Why do only callable objects get a __name__?
Steven D'Aprano
steve+comp.lang.python at pearwood.info
Mon Nov 18 18:21:15 EST 2013
On Mon, 18 Nov 2013 12:13:42 -0800, John Ladasky wrote:
> I just created an object using collections.namedtuple, and was surprised
> to discover that it didn't have a __name__
I'm not sure why you're surprised. Most objects don't have names,
including regular tuples:
py> some_tuple = (23, 42)
py> some_tuple.__name__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'tuple' object has no attribute '__name__'
Remember, Python has two distinct concepts of names: the name that an
object knows itself by, and the variable name that it is bound to.
Objects may be bound to zero, one, or more variable names. Here are some
examples of the later:
print 42 # zero name
x = 42 # one name
x = y = z = 42 # three names
It simply isn't practical or meaningful to go backwards from the object
42 to the variable name(s) it is bound to -- in the first example, there
is no variable name at all; in the third, there are three. Even if you
wanted to do it, it would be a performance killer. So if you have any
thought that "the name of an object" should be the name of the variable,
scrub that from your head, it will never fly.
That leaves us with the name that objects know themselves by. For the
rest of this post, any time I talk about a name, I always mean the name
an object knows itself by, and never the variable name it is bound to (if
there is such a variable name).
As a general rule, names aren't meaningful or useful for objects. To
start with, how would you give it a name, what syntax would you use? What
would you expect these examples to print?
import random
random.random().__name__
data = [23, 17, 99, 42]
print data[1].__name__
In general, objects are *anonymous* -- they have no inherent name.
Instances come into being in all sorts of ways, they live, they die,
they're garbage-collected by the compiler. They have no need for
individual names, and no way to be given one.
But if you insist on giving them one, you can give it a go. But I
guarantee that (1) you'll find it a lot less useful, and (2) a lot more
inconvenient than you expected:
class NamedList(list):
def __new__(cls, name, *args):
obj = super(NamedList, cls).__new__(cls, *args)
obj.__name__ = name
return obj
def __init__(self, name, *args):
super(NamedList, self).__init__(*args)
py> print NamedList("fred", [1, 2, 3, 4, 5]).__name__
fred
So it works, but what a drag, and you don't get much for the effort.
The three obvious exceptions are:
- modules
- classes/types
- functions/methods
In the first case, modules, there is an obvious interpretation of what
the name ought to be: the filename minus the extension. Since the module
knows where it came from, it can know it's own name:
py> import re
py> re.__name__
're'
Even if you bind the module object to a different variable name, it knows
its own inherent name:
py> import math as fibble
py> print fibble.__name__
math
For functions and classes, such names are especially useful, for
debugging and error messages:
py> def one_over(x):
... return 1.0/x
...
py> one_over(0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in one_over
ZeroDivisionError: float division by zero
Notice the second last line, where it gives the function's name? That
would be impossible if callables (functions, classes) didn't know their
own name. You'd get something like:
File "<stdin>", line 2, in <function object at 0xb7ea7a04>
which would be useless for debugging. So how fortunately that there is
obvious and simple syntax for setting the name of functions and classes:
class This_Is_The_Class_Name:
def this_is_the_function_name(self):
...
> -- even though something that
> behaves like __name__ is clearly accessible and printable. Here's some
> minimal Python 3.3.2 code and output:
>
> =================================================
>
> from collections import namedtuple
>
> MyNamedTupleClass = namedtuple("ANamedTuple", ("foo", "bar"))
Here you define a class, called "ANamedTuple". Unfortunately, it doesn't
use the standard class syntax, a minor limitation and annoyance of
namedtuples, and so you're forced to give the class name "ANamedTuple"
explicitly as an argument to the function call. But the important thing
here is that it is a class.
> nt = MyNamedTupleClass(1,2)
nt, on the other hand, is a specific instance of that class. You might
have hundreds, thousands, millions of such instances. Why would you want
to name them all? What point of doing so is there? They have no need, and
no benefit, to be given individual names. And consequent, when you ask
the instance "What's your name?", they respond "I don't have one":
> # print(nt.__name__) # this would raise an AttributeError
However, when you ask the class ANamedTuple what its name it, it has a
name, and can tell you:
> print(type(nt).__name__) # this is the desired output
If the instance nt claimed to be ANamedTuple, it would be *lying*. It
isn't the class, and it shouldn't claim to have the same name as the
class.
[...]
> As you can see, I snooped around in the object's type. I found that the
> type, rather than the object itself, had the __name__ I was seeking.
Yes. And why do you consider this to be a problem?
> 1. WHY do only callable objects get a __name__? A __name__ would seem
> to be a useful feature for other types. Clearly, whoever implemented
> namedtuple thought it was useful to retain and display that information
> as a part of the string representation of the namedtuple (and I agree).
Because the namedtuple that you create *is* a class. (Type and class are,
to a first approximation, synonyms.) Sadly, when you create a class using
function-call syntax rather than the class keyword, you have to manually
specify the name twice:
SomeTuple = namedtuple("SomeTuple", ...)
but that makes sense when you consider that Python has two concepts of
names. The first SomeTuple, on the left of the equals sign, is the
variable name. The second, inside the parentheses, is the class name.
They need not be the same.
> 2. If I created a superclass of namedtuple which exposed
> type(namedtuple).__name__ in the namespace of the namedtuple itself,
> would I be doing anything harmful?
Harmful? No, except perhaps helping muddy the waters between classes
(which have names) and instances (which generally don't). But why would
you bother? The right way to handle this is, when you want the name of
the type, ask for the name of the type:
type(instance).__name__
--
Steven
More information about the Python-list
mailing list