Some questions about Python3 metaclass - python

class UpperAttrMetaclass(type):
var = "test"
def __new__(upperattr_metaclass, future_class_name,
future_class_parents, future_class_attr):
print("hello world")
uppercase_attr = {}
for name, val in future_class_attr.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val
# reuse the type.__new__ method
# this is basic OOP, nothing magic in there
return type.__new__(upperattr_metaclass, future_class_name,
future_class_parents, uppercase_attr)
class Hello(object):
__metaclass__ = UpperAttrMetaclass
bar = "test"
obj = Hello()
print(obj.BAR) # obj has no attribute named BAR
Traceback (most recent call last):
File "E:\python\test.py", line 32, in
print(obj.BAR)
AttributeError: 'Hello' object has no attribute 'BAR'
Why metaclass UpperAttrMetaclass does not work?

In Python3 the way to specify a metaclass has changed from Python2 in an incompatible way.
Since Python 3.0, the way to specify a metaclass is to use the metaclass name as if it were a Named parameter on the class statement itself.
Thus, in the above example, you shuld declare your Hello class as:
class Hello(metaclass=UpperAttrMetaclass):
bar = "test"
Check the documentation at: https://docs.python.org/3.0/whatsnew/3.0.html#changed-syntax
Besides that, as you've noted, putting a __metaclass__ attribute in a c alss body is not an error, but it does nothing at all, but declaring an attribute with that name.
After a couple releases of Python3.x versions, this is the only syntactic change that is incompatible with Python 2 and can't be work-around in a straightforward way so that the code is both Python 2.x and Python 3.x compatible at the same time.
If you need the same code base to run on Python 2 and Python 3, the package named six brings the call with_metaclass which builds a dynamic class base with a syntax that is compatible with both versions.
(https://pythonhosted.org/six/#syntax-compatibility)

Related

How should we actually define constants with namespaces in Python 3? [duplicate]

I would like to store a bunch of variables under a Python namespace without creating a separate module. I notice that the result of ArgumentParser's parse_args() is a argparse.Namespace object. You can access the arguments through dot-syntax.
from argparse import ArgumentParser
parser = ArgumentParser()
# some arg definitions here...
args = parser.parse_args() # returns a `argparse.Namespace` object
How can I create the equivalent of an argparse.Namespace? I know I can do something similar with a dict but I would like to use dot-syntax. Is there any built-in class that just lets you assign arbitrary attributes?
Starting with python3.3 you can use types.SimpleNamespace.
However an alternative is simply:
class Namespace(object):
pass
namespaceA = Namespace()
namespaceA.x = 1
The full code for SimpleNamespace isn't much longer.
Note that you cannot simply use an object instance:
>>> o = object()
>>> o.x = 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'object' object has no attribute 'x'
This is because instances of object do not have a __dict__ attribute:
>>> vars(object())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: vars() argument must have __dict__ attribute
Which means you cannot set the attributes of an instance of object.
Any object subclass that does not have the __slots__ attribute set does have the __dict__ which is used (by default) to store/retrieve attributes:
>>> class Namespace(object):
... pass
...
>>> a = Namespace()
>>> a.x = 1 # same as as.__dict__['a'] = 1
>>> a.__dict__
{'x': 1}
For further information about attribute setting/lookup you should learn about descriptors.
A class can be used as a namespace, where the variables are class members:
class Namespace1:
foo = 'a'
bar = 5
To prevent callers from trying to instantiate, you can use a baseclass like:
class objectless(object):
def __new__(cls, *args, **kwargs):
raise RuntimeError('%s should not be instantiated' % cls)
And use it like:
class Namespace1(objectless):
...
It sounds like you want a python class. See the docs.
Depending on what you want exactly, you can define a bunch of variables as attributes of a class (either a variable of an instance or of the class itself) and access them that way.
If you want "the equivalent of an argparse.Namespace", use argparse.Namespace:
from argparse import Namespace
ns = Namespace(a=1)
print ns.a
If I'm understanding correctly, you want to dynamically add attributes to it. For example, a class parses command-line flags you access them directly like args.verbose, right? If so, you may be thinking of setattr() that lets you add arbitrary attributes.
class Foo(object):
pass
foo = Foo()
setattr(foo, 'ack', 'bar')
print(foo.ack) # prints 'bar'

How to set a variable's subproperty?

I am a newbie to Python. I got some Python sample code from a software vendor who extended their software API with boost.python so we can call them in Python. I am confused with some of the segments, such as:
settings = zoo.AddAnimalSettings(carni_bird_list)
settings.Name = 'birds'
settings.Type = settings.Type.enum.Bird
settings.water_min = 1, units.Litre
settings.food_min = 10, units.Gram
All the variable names are replaced to be these funny things anyway, just for explanation of the general idea.
So here the problem is in the 3rd line. How can we set the variable settings.Type with its sub property settings.Type.enum.Bird, where enum.Bird I suppose is some kind of enum of different kind of animals, which is a sub-property of settings.Type?
I tried doing some test to add one line following the above 5 lines to see if enum.Bird is still there:
settings.Type = settings.Type.enum.Bird
and it works ok.
So for this instance settings, it's sub property Type is not overwritten by its sub property of enum.Bird, it still knows enum.Bird is its sub-property.
Can you advise if I need to implement this line in Python, how can I do that?
I suppose it would be a quite interesting knowledge for people learning Python, so I raised this question here for discussing. I am trying to think in a C++ way, but I didn't figure it out.
I don't really see what's the issue. Consider an Enum defined in python:
import enum
class Type(enum.Enum):
Bird = 0
Cat = 1
The Type.Bird and Type.Cat are instances of the Type class:
>>> Type.Bird
<Type.Bird: 0>
>>> Type.Cat
<Type.Cat: 1>
As such they have access to their own class, which is Type:
>>> Type.Bird.__class__
<enum 'Type'>
Now you can just add a property to the Type class and obtain that behaviour:
class Type(enum.Enum):
Bird = 0
Cat = 1
#property
def enum(self):
return self.__class__
and now you have:
>>> Type.Bird
<Type.Bird: 0>
>>> Type.Bird.enum
<enum 'Type'>
>>> Type.Bird.enum.Bird
<Type.Bird: 0>
>>> Type.Bird.enum.Cat
<Type.Cat: 1>
Note that while the above allows you to write Bird.enum doesn't allow you to access as in Type.enum because this would return the property object.
To obtain the exact behaviour you see in that code you could:
Set the settings.Type attribute to be an instance of Type (possibly an Invalid one) and be done:
def AddAnimalSettings(*args)
settings = MyClass(*args)
settings.Type = Type.Bird
return settings
Replace the use of property with a custom made descriptor that will handle the access via the class too. In this case read the documentation about property which also provides its python code equivalent. The case you have to change is __get__ when obj is None:
class MyProperty(object):
# omissis
def __get__(self, obj, objtype=None):
if obj is None:
return objtype # <-- changed this line
if self.fget is None:
raise AttributeError("unreadable attribute")
return self.fget(obj)
Use this as:
class Type(enum.Enum):
Bird = 0
Cat = 1
#MyProperty
def enum(self):
return self.__class__
And now you have:
>>> Type.enum
<enum 'Type'>
so that Type.enum.Bird works.

Metaclass configure. Python version conflict

class UpperAttrMetaclass(type):
def __new__(cls, name, bases, attrs):
attrs["b"] = 77
return super(UpperAttrMetaclass, cls).__new__(cls, name, bases, attrs)
class Cls(object):
__metaclass__ = UpperAttrMetaclass
f = Cls()
print(f.b)
I tried to run this code on Python 3.4.3 and 2.7.11.
In 2.7.11 it works well(output - 77), but 3.4.3 throw Attribute Error
Here is the output (3.4.3)
Traceback (most recent call last):
File "D:/LABS/BSUIR_labs/4cem/PYTHON/lab2/meta.py", line 8, in <module>
print(f.b)
AttributeError: 'Cls' object has no attribute 'b'
Process finished with exit code 1
How can I fix this? metaclasses works in different way??
In python3, you declare the metaclass inline:
class Cls(object, metaclass=UpperAttrMetaclass):
...
I think that 2to3 should handle this, but if you need to support both in the same source file without using 2to3, then you'll probably want some sort of shimming library. six is the de-facto standard here. You'll want to use six.with_metaclass:
import six
class Cls(six.with_metaclass(UpperAttrMetaclass, object)):
...

Python: dynamically add attributes to new-style class/obj

Can I dynamically add attributes to instances of a new-style class (one that derives from object)?
Details:
I'm working with an instance of sqlite3.Connection. Simply extending the class isn't an option because I don't get the instance by calling a constructor; I get it by calling sqlite3.connect().
Building a wrapper doesn't save me much of the bulk for the code I'm writing.
Python 2.7.1
Edit
Right answers all. But I still am not reaching my goal; instances of sqlite3.Connection bar my attempts to set attributes in the following ways (as do instances of object itself). I always get an AttributeError:
> conn = sqlite3.connect([filepath])
> conn.a = 'foo'
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
conn.a = 'foo'
AttributeError: 'object' object has no attribute 'a'
> conn.__setattr__('a','foo')
Traceback (most recent call last):
File "<pyshell#2>", line 1, in <module>
conn.__setattr__('a','foo')
AttributeError: 'object' object has no attribute 'a'
Help?
Yes, unless the class is using __slots__ or preventing attribute writing by overriding __setattr__, or an internal Python class, or a Python class implemented natively (usually in C).
You can always try setting an attribute. Except for seriously weird __setattr__ implementations, assigning an attribute to an instance of a class of one of the types mentioned above should raise an AttributeError.
In these cases, you'll have to use a wrapper, like this:
class AttrWrapper(object):
def __init__(self, wrapped):
self._wrapped = wrapped
def __getattr__(self, n):
return getattr(self._wrapped, n)
conn = AttrWrapper(sqlite3.connect(filepath))
Simple experimentation:
In []: class Tst(object): pass
..:
In []: t= Tst()
In []: t.attr= 'is this valid?'
In []: t.attr
Out[]: 'is this valid?'
So, indeed it seems to be possible to do that.
Update:
But from the documentation: SQLite is a C library that ..., so it seems that you really need to wrap it.
conn.a = 'foo',
or any dynamic assignment is valid, if conn is
<type 'classobj'>.
Things like:
c=object()
c.e=1
will raise an Attribute error. On the otherhand: Python allows you to do fantastic Metaclass programming:
>>>from new import classobj
>>>Foo2 = classobj('Foo2',(Foo,),{'bar':lambda self:'bar'})
>>>Foo2().bar()
>>>'bar'
>>>Foo2().say_foo()
>>>foo

Extension methods in Python

Does Python have extension methods like C#? Is it possible to call a method like:
MyRandomMethod()
on existing types like int?
myInt.MyRandomMethod()
You can add whatever methods you like on class objects defined in Python code (AKA monkey patching):
>>> class A(object):
>>> pass
>>> def stuff(self):
>>> print self
>>> A.test = stuff
>>> A().test()
This does not work on builtin types, because their __dict__ is not writable (it's a dictproxy).
So no, there is no "real" extension method mechanism in Python.
It can be done with Forbidden Fruit (https://pypi.python.org/pypi/forbiddenfruit)
Install forbiddenfruit:
pip install forbiddenfruit
Then you can extend built-in types:
>>> from forbiddenfruit import curse
>>> def percent(self, delta):
... return self * (1 + delta / 100)
>>> curse(float, 'percent', percent)
>>> 1.0.percent(5)
1.05
Forbidden Fruit is fundamentally dependent on the C API, it works only on cpython implementations and won’t work on other python implementations, such as Jython, pypy, etc.
not sure if that what you're asking but you can extend existing types and then call whatever you like on the new thing:
class int(int):
def random_method(self):
return 4 # guaranteed to be random
v = int(5) # you'll have to instantiate all you variables like this
v.random_method()
class int(int):
def xkcd(self):
import antigravity
print(42)
>>>v.xkcd()
Traceback (most recent call last):
File "<pyshell#81>", line 1, in <module>
v.xkcd()
AttributeError: 'int' object has no attribute 'xkcd'
c = int(1)
>>> c.random_method()
4
>>> c.xkcd()
42
hope that clarifies your question
The following context manager adds the method like Forbidden Fruit would without the limitations of it. Besides that it has the additional benefit of removing the extension method afterwards:
class extension_method:
def __init__(self, obj, method):
method_name = method.__name__
setattr(obj, method_name, method)
self.obj = obj
self.method_name = method_name
def __enter__(self):
return self.obj
def __exit__(self, type, value, traceback):
# remove this if you want to keep the extension method after context exit
delattr(self.obj, self.method_name)
Usage is as follows:
class C:
pass
def get_class_name(self):
return self.__class__.__name__
with extension_method(C, get_class_name):
assert hasattr(C, 'get_class_name') # the method is added to C
c = C()
print(c.get_class_name()) # prints 'C'
assert not hasattr(C, 'get_class_name') # the method is gone from C
I've had great luck with the method described here:
http://mail.python.org/pipermail/python-dev/2008-January/076194.html
I have no idea if it works on builtins though.
Another option is to override the meta-class. This allows you to, among other things, specify functions that should exist in all classes.
This article starts to discuss it:
http://www.onlamp.com/pub/a/python/2003/04/17/metaclasses.html

Categories

Resources