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.
Related
I was wondering what the best way to implement the following design would be in Python:
class Executor:
def __init__(self):
self.val = 5
def action(self):
self.action(self.val)
#classmethod
def action(cls, val):
print(f"Val is: {val}")
I want to be able to access the method both as an instance method that uses the value the object was initialised with, and as a class method which uses a passed in variable. Here is an example of the ways in which I would like to call it:
>>> Executor.action(3)
Val is: 3
>>> Executor().action()
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: action() missing 1 required positional argument: 'val'
I was thinking about trying to use keyword arguments, but I can't seem to get that to work either. Here is my code so far:
class Executor:
def __init__(self):
self.val = 5
#classmethod
def action(cls, val=None):
if val is None:
# This doesn't work; cls does not have attribute 'val'.
if hasattr(cls, "val"):
print(f"Val from instance: {cls.val}")
else:
raise ValueError("Called as class method and val not passed in.")
else:
print(f"Val passed in: {val}")
>>> Executor.action(3)
Val passed in: 3
>>> Executor().action()
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "<input>", line 13, in action
ValueError: Called as class method and val not passed in.
But the class instance does not have the val available for access.
The only other thing I can think of is using Hungarian notation, but it's not ideal as it's a bit messy, and it means there's multiple method names.
class Executor:
def __init__(self):
self.val = 5
def action_instance(self):
self.action_class(self.val)
#classmethod
def action_class(cls, val):
print(f"Val is: {val}")
>>> Executor.action_class(3)
Val is: 3
>>> Executor().action_instance()
Val is: 5
Any advice on a solid, clean approach would be greatly appreciated!
Cheers.
What you want to do looks strange to me, I am not sure you need that. Python cannot overload by method signatures/type, although there is the functools.singledispatch. So by defining action a second time you are actually replacing the first definition for the method.
The observable behaviour can be achieved with:
class Executor:
#classmethod
def action(cls, val=5):
return val
print(Executor().action())
print(Executor.action(3))
Outputs:
5
3
But again check first that you really need something like that, because it breaks one of the expectations of Python coders and Python data model: calling a method through the class is equivalent to calling the method through the instance given that you pass the instance to the class method.
obj = Executor()
obj.action() # this is the same as Executor.action(obj)
I have a code like this:
class Base:
def __init__(self):
pass
def new_obj(self):
return Base() # ← return Derived()
class Derived(Base):
def __init__(self):
pass
In the line with a comment I actually want not exactly the Derived object, but any object of class that self really is.
Here is a real-life example from Mercurial.
How to do that?
def new_obj(self):
return self.__class__()
I can't think of a really good reason to do this, but as D.Shawley pointed out:
def new_obj(self):
return self.__class__()
will do it.
That's because when calling a method on a derived class, if it doesn't exist on that class, it will use the method resolution order to figure out which method to call on its inheritance chain. In this case, you've only got one, so it's going to call Base.new_obj and pass in the instance as the first argument (i.e. self).
All instances have a __class__ attribute, that refers to the class that they are an instance of. So given
class Base:
def new_obj(self):
return self.__class__()
class Derived(Base): pass
derived = Derived()
The following lines are functionally equivalent:
derived.new_obj()
# or
Base.new_obj(derived)
You may have encountered a relative of this if you've either forgotten to add the self parameter to your function declaration, or not provided enough arguments to a function and seen a stack trace that looks like this:
>>> f.bar()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: bar() takes exactly 2 arguments (1 given)
You can use a classmethod:
class Base:
def __init__(self):
pass
#classmethod
def new_obj(cls):
return cls()
class Derived(Base):
def __init__(self):
pass
>>> b = Base()
>>> b.new_obj()
<__main__.Base at 0x10fc12208>
>>> d = Derived()
>>> d.new_obj()
<__main__.Derived at 0x10fdfce80>
You can also do this with a class method, which you create with a decorator.
In [1]: class Base:
...: #classmethod
...: def new_obj(cls):
...: return cls()
...:
In [2]: class Derived(Base): pass
In [3]: print type(Base.new_obj())
<type 'instance'>
In [4]: print Base.new_obj().__class__
__main__.Base
In [5]: print Derived.new_obj().__class__
__main__.Derived
Incidentally (you may know this), you don't have to create __init__ methods if you don't do anything with them.
Assume that I have a class as following:
class MyClass(object):
def __init__(self, value=None):
self.attr = value
#property
def attr(self):
# This acts as a getter?
# Let's call the function "attr_1" as alias
return self.__attr
#attr.setter
def attr(self, value):
# This acts as a setter?
# Let's call the function "attr_2" as alias
self.__attr = value
inst = MyClass(1)
I read the Documentation on Descriptor and looked at the implementation of property class.
As far as I know, when I type inst.attr, the following happens:
The first attr (whose alias is attr_1) is found, and attr is now an instance of property class, which is a data descriptor.
Therefore, it will override the instance dictionary, which means type(inst).__dict__['attr'].__get__(inst, type(inst)) is invoked.
attr.__get__(inst, type(inst)) invokes attr.fget(inst), where fget() is in fact the attr(self) (the "raw" attr_1 function).
Finally, attr.fget(inst) returns inst.__attr.
Here comes the first question: the class MyClass does not have an attribute __attr, then how to interpret inst.__attrin step 3?
Similarly, in the emulated setter, how does Python find an attribute inst.__attr to assign the value?
And a trivial question: since property is a class, why not Property instead of property?
Your question is not directly related to properties actually, and the way they work as data descriptors. It's just the way Python fakes private attributes marked as starting with two underscores.
>>> inst.__attr
Traceback (most recent call last):
File "<pyshell#4>", line 1, in <module>
inst.__attr
AttributeError: 'MyClass' object has no attribute '__attr'
Consider that you wrote your code using an internal variable with a single underscore (usually the convention to say, you shouldn't touch this but I won't enforce, do at your own risk):
>>> class MyClass2(object):
def __init__(self, value=None):
self.attr = value
#property
def attr(self):
# This acts as a getter?
# Let's call the function "attr_1" as alias
return self._attr
#attr.setter
def attr(self, value):
# This acts as a setter?
# Let's call the function "attr_2" as alias
self._attr = value
>>> inst2 = MyClass2(1)
>>> inst2._attr
1
And you can see the trick by peeking at the object's __dict__
>>> inst2.__dict__
{'_attr': 1}
>>> inst.__dict__
{'_MyClass__attr': 1}
Just some more to convince you that this has nothing to do with properties:
>>> class OtherClass(object):
def __init__(self, value):
self.__attr = value
def get_attr(self):
return self.__attr
def set_attr(self, value):
self.__attr = value
>>> other_inst = OtherClass(1)
>>> other_inst.get_attr()
1
>>> other_inst.__attr
Traceback (most recent call last):
File "<pyshell#17>", line 1, in <module>
other_inst.__attr
AttributeError: 'OtherClass' object has no attribute '__attr'
>>> other_inst.__dict__
{'_OtherClass__attr': 1}
>>> other_inst._OtherClass__attr
1
>>> other_inst._OtherClass__attr = 24
>>> other_inst.get_attr()
24
>>> inst._MyClass__attr = 23
>>> inst.attr
23
Concerning your last question, I just don't think there is such a convention in Python that classes must start with an uppercase. property is not an isolated case (datetime, itemgetter, csv.reader, ...).
Looking to find a solution(not sure if it exists!) to the following situation:
Starting point is a dictionary dict = {k1:v1, k2:v2,...,kn:vn} where n is not fixed.
Is there a way to write a generic class that will have n methods generated dynamically that can be called as in the following example:
class example(dict):
example.k1()
example.k2()
.
.
.
example.kn()
Eachexample.ki()where 1<=i<=n, should return the corresponding vi.
Instead of creating so many method dynamically better override __getattr__ method of your class and return a callable from there:
class Example(dict):
def __getattr__(self, k):
if k in self:
return lambda: self[k]
raise TypeError('Example object has not attribute {!r}'.format(k))
Note that for keys like keys(), items(), etc __getattr__ won't be called as they are found in the class by __getattribute__ itself. And better don't name any of your keys after them.
Demo:
>>> d = Example(a=1, b=2, c=3)
>>> d.a()
1
>>> d.b()
2
>>> d.foo()
Traceback (most recent call last):
File "<pyshell#14>", line 1, in <module>
d.foo()
File "/home/ashwini/py/so.py", line 7, in __getattr__
raise TypeError('Example object has not attribute {!r}'.format(k))
TypeError: Example object has not attribute 'foo'
What you want is to override the __getattr__ function described here.
To take your example:
class example(dict):
def __getattr__(self, name):
return lambda: self[name]
This allows you to do:
e = example()
e["foo"] = 1
print e.foo()
==> 1
I think adding a method to class dynamically can help u.
class example(object) :
dict={'k1':'v1','k2':'v2','k3':'v3','kn':'vn'}
def getvalue(self,key) :
return self.dict[key]
if __name__=="__main__" :
e = example()
e.method1=e.getvalue # this is adding a method to example class dynamically.
print e.method1('k1')
e.method2=e.getvalue
print e.method2('k2')
e.method3=e.getvalue
print e.method3('k3')
e.methodn=e.getvalue
print e.methodn('kn')
this outputs
v1
v2
v3
vn
I have a method in my Customer class called save_from_row(). It looks like this:
#classmethod
def save_from_row(row):
c = Customer()
c.name = row.value('customer', 'name')
c.customer_number = row.value('customer', 'number')
c.social_security_number = row.value('customer', 'social_security_number')
c.phone = row.value('customer', 'phone')
c.save()
return c
When I try to run my script, I get this:
Traceback (most recent call last):
File "./import.py", line 16, in <module>
Customer.save_from_row(row)
TypeError: save_from_row() takes exactly 1 argument (2 given)
I don't understand the mismatch in the number of arguments. What's going on?
The first argument to a classmethod is the class itself. Try
#classmethod
def save_from_row(cls, row):
c = cls()
# ...
return c
or
#staticmethod
def save_from_row(row):
c = Customer()
# ...
return c
The classmethod variant will enable to create subclasses of Customer with the same factory function.
Instead of the staticmethod variant, I'd usually use module-level functions.
You want:
#classmethod
def save_from_row(cls, row):
Class methods get the method's class as the first argument.