How to "uninstantiate" an object? - python

I wrote a function in Python:
def instantiate(c):
if inspect.isclass(c): return c()
elif isinstance(c, object): return c
else: raise Exception, '%s is not an object or class.' % c
Now I want to do the opposite: get the class from an already instantiated object so that I can re-instantiate it with different parameters. How can I do that?
Tests:
>>> f = Form()
>>> type(f)()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: instance() takes at least 1 argument (0 given)
>>> f.__class__()
<forms.Form instance at 0xb7f4d5cc>
More tests:
>>> o = object()
>>> type(o)()
<object object at 0xb7f78478>
>>> o.__class__()
<object object at 0xb7f78480>
Seems to work for object but not my Form class:
class Form:
def __init__(self, data={}, prefix='', action='', id=None):
I'm guessing this has something to do with self but I don't know what.

To get the class of x
x.__class__

The class of object c is type(c).

Related

Python, why is the dataclass attribute read-only?

I have a dataclass which its signature gets updated based on the active configs before it gets initiated and may be modified after initiation. Accordingly, I have something like this:
from dataclasses import dataclass, field
#dataclass(slots=True, frozen=False)
class MyClass:
att_1: str = field(init=False)
att_2: str = field(init=False)
#classmethod
def update_class(cls, first, second):
cls.att_1 = first
cls.att_2 = second
My problem is that although the dataclass is not frozen, when I initiate the class I cannot change the attributes anymore. This is a sample of what I do.
MyClass.update_class("11", "12")
print(MyClass.att_1)
a = MyClass()
print(a)
print(MyClass.att_2)
print(MyClass.att_1)
a.att_2 = "002"
print(a)
Running this I get what is shown bellow:
11
MyClass(att_1='11', att_2='12')
12
11
Traceback (most recent call last):
File "/b.py", line 25, in <module>
a.att_2 = "002"
AttributeError: 'MyClass' object attribute 'att_2' is read-only
Can someone tell me why is att_2 read-only and how can I fix this?
So, the problem is that you are using __slots__ and then removing the __slots__ descriptors, breaking your class. So, consider (without the dataclass involved):
>>> class Foo:
... __slots__ = "x", "y"
...
>>> foo = Foo()
>>> foo.x = 1
>>> foo.x
1
>>> Foo.x
<member 'x' of 'Foo' objects>
>>> Foo.x = None
>>> foo.x
>>> foo.x = 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object attribute 'x' is read-only
So, one simple solution is to not use __slots__, but it really isn't clear to me how this is all supposed to work or why you have an update_class method (and what it's supposed to accomplish).

Why do Python's empty classes and functions work as arbitrary data containers, but not other objects?

I've seen two different Python objects used to group arbitrary data together: empty classes and functions.
def struct():
pass
record = struct
record.number = 3
record.name = "Zoe"
class Struct:
pass
record = Struct()
record.number = 3
record.name = "Zoe"
Even if the class isn't empty, it seems to work so long as it's defined at runtime.
But when I got cocky and tried to do this with built-in functions or classes, it didn't work.
record = set()
record.number = 3
AttributeError: 'set' object has no attribute 'number'
record = pow
pow.number = 3
AttributeError: 'builtin_function_or_method' object has no attribute 'number'
Is there a fundamental difference between built-in and "custom" classes and functions that accounts for this behavior?
The difference is that both function objects and your Struct object have a __dict__ attribute, but set instances and built-in functions do not:
>>> def struct():
... pass
...
>>> record = struct
>>> record.number = 2
>>> struct.__dict__
{'number': 2}
>>> class Struct:
... pass
...
>>> record = Struct()
>>> record.number = 3
>>> record.__dict__
{'number': 3}
>>> record=set()
>>> record.__dict__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'set' object has no attribute '__dict__'
>>> pow.__dict__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'builtin_function_or_method' object has no attribute '__dict__'
In classes you can emulate the behavour using slots (although only on new-style classes):
>>> class StructWithSlots(object):
... __slots__ = []
...
>>> record = StructWithSlots()
>>> record.number = 3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'StructWithSlots' object has no attribute 'number'
>>> record.__dict__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'StructWithSlots' object has no attribute '__dict__'
Built-in types are written in C and cannot be modified like that. But after the type/class unification introduced in py2.2 you can now inherit from a built-in types and override or add your own attributes to that subclass.
You can use the forbiddenfood package to add attributes to built-in types:
This project aims to give you the way to find heaven in tests, but it
might lead you to hell if you use it on production code.
>>> from forbiddenfruit import curse
>>> def words_of_wisdom(self):
... return self * "blah "
>>> curse(int, "words_of_wisdom", words_of_wisdom)
>>> assert (2).words_of_wisdom() == "blah blah "
And of course if you're cocky enough then you can create your own types in C and add such features to it.
Some built-ins can be more restrictive. Also, classes implemented with slots won't accept arbitrary attributes either.
If you want some simular protection in your own class, you can use the __setattr__() method.
class TestClass(object):
# Accept the attributes in this list
__valid_attributes = ["myattr1", "myattr2"]
def __setattr__(self, name, value):
if not name in TestClass.__valid_attributes:
raise AttributeError(
"{0} has no attribute '{1}'".format(self.__class__.__name__, name))
self.__dict__[name] = value
Now you can do something like this:
t = TestClass()
t.noattr = "test" # AttributeError: TestClass has no attribute 'noattr'
But "valid attributes" can still be set:
t = TestClass()
t.myattr1 = "test"
print(t.myattr1) # test

Impossible to set an attribute to a string?

Usually, you can set an arbitrary attribute to a custom object, for instance
----------
>>> a=A()
>>> a.foo=42
>>> a.__dict__
{'foo': 42}
>>>
----------
On the other hand, you can't do the same binding with a string object :
----------
>>> a=str("bar")
>>> a.foo=42
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'foo'
>>> a.__dict__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute '__dict__'
>>>
----------
Why ?
Because the str type is a type wich does not has an attribute dict. From the docs, "Classes" section:
A class has a namespace implemented by a dictionary object.
Class attribute references are translated to lookups in this
dictionary, e.g., C.x is translated to C.__dict__["x"]
You can also enforce something similar on custom objects:
>>> class X(object):
... __slots__=('a', )
...
>>> a = X()
>>> a.a = 2
>>> a.foo = 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'X' object has no attribute 'foo'
In general, you should not be setting nor modifying fields of objects that you are not supposed to. The documentation of the specific data type should reference you what fields are available for public modification.
For example, an ReadOnlyPoint object, where the x and y coordinates are set only on object construction:
>>> class ReadOnlyPoint(object):
... __slots__ = ('_x', '_y')
... def __init__(self, x, y):
... self._x = x
... self._y = y
... def getx(self):
... return self._x
... def gety(self):
... return self._y
... x = property(getx)
... y = property(gety)
...
>>> p = ReadOnlyPoint(2, 3)
>>> print p.x, p.y
2 3
>>> p.x = 9
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't set attribute
>>> p._x = 9
>>> print p.x, p.y
9 3
While the x and y properties are read-only, accessing the object internals allows you to alter the object's state.
The inhability to add a new field to an str object is an implementation detail, specific to the Python version that you are using.
http://docs.python.org/reference/datamodel.html
If the class has a setattr() or delattr() method, this is
called instead of updating the instance dictionary directly.
http://docs.python.org/reference/datamodel.html#object.setattr

How to subclass a subclass of numpy.ndarray

I'm struggling to subclass my own subclass of numpy.ndarray. I don't really understand what the problem is and would like someone to explain what goes wrong in the following cases and how to do what I'm trying to do.
What I'm trying to achieve:
I have a subclass of numpy.ndarry that behaves as I want (class A in the code below). I want to subclass A (class B in the code below) so that B contains additional information (name) and methods (the decorated .simple_data method).
Case 1:
import numpy as np
class A(np.ndarray):
def __new__(cls,data):
obj = np.asarray(data).view(cls)
return obj
def __array_finalize(self,obj):
if obj is None: return
class B(A):
def __init__(self,data,name):
super(B,self).__init__(data)
self.name = name
#property
def simple_data(self):
return [data[0,:],data[:,0]]
if __name__ == '__main__':
data = np.arange(20).reshape((4,5))
b = B(data,'B')
print type(b)
print b.simple_data
Running this code produces the output:
Traceback (most recent call last):
File "ndsubclass.py", line 24, in <module>
b = B(data,'B')
TypeError: __new__() takes exactly 2 arguments (3 given)
I assume that this is related to the 'name' variable in the construction of B and that due to A being a subclass of numpy.array, A's new method is being called before B's init method. Thus to fix this I assume that B also needs a new method that appropriately handles the additional argument.
My guess is something like:
def __new__(cls,data,name):
obj = A(data)
obj.name = name
return obj
should do it, but how do I change the class of obj?
Case 2:
import numpy as np
class A(np.ndarray):
def __new__(cls,data):
obj = np.asarray(data).view(cls)
return obj
def __array_finalize__(self,obj):
if obj is None: return
class B(A):
def __new__(cls,data):
obj = A(data)
obj.view(cls)
return obj
def __array_finalize__(self,obj):
if obj is None: return
#property
def simple_data(self):
return [self[0,:],self[:,0]]
if __name__ == '__main__':
data = np.arange(20).reshape((4,5))
b = B(data)
print type(b)
print b.simple_data()
When run the output is:
<class '__main__.A'>
Traceback (most recent call last):
File "ndsubclass.py", line 30, in <module>
print b.simple_data()
AttributeError: 'A' object has no attribute 'simple_data'
This surprises me as I was expecting:
<class '__main__.B'>
[array([0, 1, 2, 3, 4]), array([ 0, 5, 10, 15])]
I assume that the call to view() in B.new() is somehow not correctly setting the class of obj. Why?
I'm confused as to what is going on and would be very grateful if someone could explain it.
For Case 1, the simplest way is:
class B(A):
def __new__(cls,data,name):
obj = A.__new__(cls, data)
obj.name = name
return obj
__new__ is actually a static method that takes a class as the first argument, not a class method, so you can call it directly with the class of which you want to create an instance.
For Case 2, view doesn't work in-place, you need to assign the result to something, the simplest way is:
class B(A):
def __new__(cls,data):
obj = A(data)
return obj.view(cls)
Also, you've got __array_finalize__ defined the same in A and B there (probably just a typo) -- you don't need to do that.

How to assign a new class attribute via __dict__?

I want to assign a class attribute via a string object - but how?
Example:
class test(object):
pass
a = test()
test.value = 5
a.value
# -> 5
test.__dict__['value']
# -> 5
# BUT:
attr_name = 'next_value'
test.__dict__[attr_name] = 10
# -> 'dictproxy' object does not support item assignment
There is a builtin function for this:
setattr(test, attr_name, 10)
Reference: http://docs.python.org/library/functions.html#setattr
Example:
>>> class a(object): pass
>>> a.__dict__['wut'] = 4
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'dictproxy' object does not support item assignment
>>> setattr(a, 'wut', 7)
>>> a.wut
7

Categories