-1

I define some func here, it will change all user defined attribtutes into upper case

def up(name, parent, attr):
    user_defined_attr = ((k, v) for k, v in attr.items() if not k.startswith('_'))
    up_attr = {k.upper(): v for k,v in user_defined_attr}
    return type(name, parent, up_attr)

For example:

my_class = up('my_class', (object,), {'some_attr': 'some_value'})

hasattr(my_class, 'SOME_ATTR')
True

Here is some words from python doc about metaclass

https://docs.python.org/2/reference/datamodel.html?highlight=metaclass#metaclass

The appropriate metaclass is determined by the following precedence rules:

If dict['__metaclass__'] exists, it is used.
Otherwise, if there is at least one base class, its metaclass is used (this looks for a __class__ attribute first and if not found, uses its type).
Otherwise, if a global variable named __metaclass__ exists, it is used.
Otherwise, the old-style, classic metaclass (types.ClassType) is used.

So I did some test

>>> def up(name, parent, attr):
...     user_defined_attr = ((k, v) for k, v in attr.items() if not k.startswith('_'))
...     up_attr = {k.upper(): v for k,v in user_defined_attr}
...     return type(name, parent, up_attr)
... 
>>> 
>>> 
>>> __metaclass__ = up
>>> 
>>> class C1(object):
...     attr1 = 1
... 
>>> hasattr(C1, 'ATTR1')
False

Not working for the global var case, why?

Arne
  • 17,706
  • 5
  • 83
  • 99
Kramer Li
  • 2,284
  • 5
  • 27
  • 55

2 Answers2

1

If you're on Python 2, your problem is that you listed object as a base class for C1, and the global __metaclass__ fallback is lower-priority than the base class's metaclass.

If you're on Python 3, your problem is that global __metaclass__ doesn't do anything any more.

user2357112
  • 260,549
  • 28
  • 431
  • 505
1

It seems that only old-style classes use the global __metaclass__ variable. The fact that they are old-style has nothing to do with that, but it is the way they are defined. New-style classes explicitly inherit from a class with a meta-class whereas old-style classes don't.

class Meta(type):
    pass


__metaclass__ = Meta


class NewStyle(object):
    pass

class OldStyle:
    pass


print "new style", type(NewStyle)
print "old style", type(OldStyle)

This code prints:

new style <type 'type'>
old style <class '__main__.Meta'>

This seems to be consistent with the rules you listed. NewStyle has the base class object and that class has its own meta class type. So for new-style classes type is chosen according to the seconds rule.

Wombatz
  • 4,958
  • 1
  • 26
  • 35
  • It's not a matter of old-style or new-style. An old-style class wouldn't use a global `__metaclass__` either if any base classes were listed. Also, your `OldStyle` class is really new-style, as you can see by checking `isinstance(OldStyle, type)`. – user2357112 May 09 '19 at 10:29
  • @user2357112 `isinstance(OldStyle, type)` returns `False` for me as it should, or am i missing something? I agree that the difference in behavior is not caused by old-style/new-style but by the way they are defined. – Wombatz May 09 '19 at 10:35
  • @user2357112 Also, even if an old-style class inherits from another old-style class without a meta class, then the global variable will be used. – Wombatz May 09 '19 at 10:45
  • [I see True.](https://ideone.com/yOCC2r) Also, [here's a demo of an old-style class inheriting from another old-style class and not using the global](https://ideone.com/XVj6AP). – user2357112 May 09 '19 at 14:45