how to dynamically generate __name__ for an object?

E

Eric Snow

Thought I knew how to provide a dynamic __name__ on instances of a
class. My first try was to use a non-data descriptor:

# module base.py

class _NameProxy(object):
def __init__(self, oldname):
self.oldname = oldname
def __get__(self, obj, cls):
if obj is None:
return self.oldname
if "__name__" not in obj.__dict__:
return str(obj.__context__)
return obj.__name__

class _BaseMeta(type):
def __init__(cls, name, bases, namespace):
cls.__name__ = _NameProxy(name)

class Base(object):
__metaclass__ = _BaseMeta


$ python _base.py
Traceback (most recent call last):
...
File "/usr/local/lib/python2.4/site-packages/base.py", line xx, in __init__
cls.__name__ = _NameProxy(name)
TypeError: Error when calling the metaclass bases
can only assign string to Base.__name__, not '_NameProxy'


Needless to say I was surprised. After looking in typeobject.c, I
believe that __name__ must be a string where classes are concerned[1].
So if I want all my instances to have a __name__ attribute, and for
it to be dynamically provided if it isn't set on the instance, what
are my options? Or maybe I did something wrong and it should work as
I expected?

-eric


[1] http://hg.python.org/cpython/file/default/Objects/typeobject.c#l244
 
S

Steven D'Aprano

Eric said:
Thought I knew how to provide a dynamic __name__ on instances of a
class. My first try was to use a non-data descriptor:

Perhaps you should explain what you are trying to do. If you want to give
instances their own name, why not just give them an instance
attribute "name"? Double-leading-and-trailing-underscore attributes are
reserved for Python, and have special semantics.
 
E

Eric Snow

Perhaps you should explain what you are trying to do. If you want to give
instances their own name, why not just give them an instance
attribute "name"? Double-leading-and-trailing-underscore attributes are
reserved for Python, and have special semantics.

Again useful information failed to make it from my head to the
keyboard. :) Currently I am looking at objects, regardless of their
types, and keying off their name. If it is a module, class, or
function object, the name should be stored in __name__ already. If it
is an instance of the Base class I demonstrated, I would like to
maintain that convention.

However, you may have a good point. In other code I can simply check
for the name attribute I choose for my objects and also check for
__name__. I suppose I just liked the idea of having a single
attribute to check.

-eric
 
F

Fuzzyman

Thought I knew how to provide a dynamic __name__ on instances of a
class.  My first try was to use a non-data descriptor:

# module base.py

class _NameProxy(object):
    def __init__(self, oldname):
        self.oldname = oldname
    def __get__(self, obj, cls):
        if obj is None:
            return self.oldname
        if "__name__" not in obj.__dict__:
            return str(obj.__context__)
        return obj.__name__

class _BaseMeta(type):
    def __init__(cls, name, bases, namespace):
        cls.__name__ = _NameProxy(name)

class Base(object):
    __metaclass__ = _BaseMeta

$ python _base.py
Traceback (most recent call last):
  ...
  File "/usr/local/lib/python2.4/site-packages/base.py", line xx, in __init__
    cls.__name__ = _NameProxy(name)
TypeError: Error when calling the metaclass bases
    can only assign string to Base.__name__, not '_NameProxy'

Needless to say I was surprised.  After looking in typeobject.c, I
believe that __name__ must be a string where classes are concerned[1].
 So if I want all my instances to have a __name__ attribute, and for
it to be dynamically provided if it isn't set on the instance, what
are my options?  Or maybe I did something wrong and it should work as
I expected?

__name__ can be a descriptor, so you just need to write a descriptor
that can be fetched from classes as well as instances.

Here's an example with a property (instance only):
.... @property
.... def __name__(self):
.... return 'bar'
....'bar'

Michael
 
I

Ian Kelly

__name__ can be a descriptor, so you just need to write a descriptor
that can be fetched from classes as well as instances.

Here's an example with a property (instance only):
...   @property
...   def __name__(self):
...     return 'bar'
...

But:
Foo.__name__ 'Foo'
repr(Foo())
' said:
Foo.__dict__['__name__']
<property object at 0x00CBA6F0>

It seems that Foo.__name__ and Foo.__dict__['__name__'] are not the
same thing, and Python itself only uses the former.
 
F

Fuzzyman


That's why I said "you just need to _write_ a descriptor that can be
fetched from classes as well as instances". The example with property
was to show that it *could* be a descriptor. You can write descriptors
with custom behaviour when fetched from a class.

However it turns out that you're right and I'm wrong; __name__ is
special:
.... def __get__(*args):
.... return 'bar'
........ __name__ = descriptor()
........ name = descriptor()
....'bar'

As Eric points out in his original slot, types have their __name__
slot filled in with a string in typeobject.c

All the best,

Michael
 
E

Eric Snow

Thought I knew how to provide a dynamic __name__ on instances of a
class.  My first try was to use a non-data descriptor:

# module base.py

class _NameProxy(object):
    def __init__(self, oldname):
        self.oldname = oldname
    def __get__(self, obj, cls):
        if obj is None:
            return self.oldname
        if "__name__" not in obj.__dict__:
            return str(obj.__context__)
        return obj.__name__

class _BaseMeta(type):
    def __init__(cls, name, bases, namespace):
        cls.__name__ = _NameProxy(name)

class Base(object):
    __metaclass__ = _BaseMeta

$ python _base.py
Traceback (most recent call last):
  ...
  File "/usr/local/lib/python2.4/site-packages/base.py", line xx, in __init__
    cls.__name__ = _NameProxy(name)
TypeError: Error when calling the metaclass bases
    can only assign string to Base.__name__, not '_NameProxy'

Needless to say I was surprised.  After looking in typeobject.c, I
believe that __name__ must be a string where classes are concerned[1].
 So if I want all my instances to have a __name__ attribute, and for
it to be dynamically provided if it isn't set on the instance, what
are my options?  Or maybe I did something wrong and it should work as
I expected?

__name__ can be a descriptor, so you just need to write a descriptor
that can be fetched from classes as well as instances.

Here's an example with a property (instance only):
...   @property
...   def __name__(self):
...     return 'bar'
...'bar'

Thanks! Your example is exactly what I didn't try in the first place,
but should have. I was thinking of __name__ on a class as a simple
data attribute. My original worry was that by adding a descriptor for
__name__ on my class, I would overwrite the name of the class, hence
the elaborate descriptor.

However, for type objects (classes) __name__ is a data descriptor and
the actual name isn't stored in __name__. I was staring that right in
the face (the link I provided is for the setter method of the __name__
"property" for type objects). I guess it was a "forest for the trees"
moment.

Because of attribute lookup in Python, "Base.__name__" uses the
equivalent of "Base.__class__.__name__.__get__(..., Base,
Base.__class__)".

Anyway, thanks for making me see how dumb I am! <wink>

-eric
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,901
Latest member
Noble71S45

Latest Threads

Top