Overriding inherited method without name mangling [duplicate] - python

This question already has answers here:
How do I call a parent class's method from a child class in Python?
(16 answers)
Closed 5 years ago.
Note: similar question here, but I don't believe it's an exact duplicate given the specifications.
Below, I have two classes, one inheriting from the other. Please note these are just illustrative in nature.
In _Pandas.array(), I want to simply wrap a pandas DataFrame around the NumPy array returned from _Numpy.array(). I'm aware of what is wrong with my current code (_Pandas.array() gets redefined, attempts to call itself, and undergoes infinite recursion), but not how to fix it without name mangling or quasi-private methods on the parent class.
import numpy as np
import pandas as pd
class _Numpy(object):
def __init__(self, x):
self.x = x
def array(self):
return np.array(self.x)
class _Pandas(_Numpy):
def __init__(self, x):
super(_Pandas, self).__init__(x)
def array(self):
return pd.DataFrame(self.array())
a = [[1, 2], [3, 4]]
_Pandas(a).array() # Intended result - pd.DataFrame(np.array(a))
# Infinite recursion as method shuffles back & forth
I'm aware that I could do something like
class _Numpy(object):
def __init__(self, x):
self.x = x
def _array(self): # Changed to leading underscore
return np.array(self.x)
class _Pandas(_Numpy):
def __init__(self, x):
super().__init__(x)
def array(self):
return pd.DataFrame(self._array())
But this seems very suboptimal. In reality, I'm using _Numpy frequently--it's not just a generic parent class--and I'd prefer not to preface all its methods with a single underscore. How else can I go about this?

Uhm... just want to check why in _Pandas class you don't call super directly?
class _Pandas(_Numpy):
def __init__(self, x):
super(_Pandas,self).__init__(x)
def array(self):
return pd.DataFrame(super(_Pandas,self).array())
I tried that and got the below result, don't know if it's what you wanted or I have missed anything
a = [[1, 2], [3, 4]]
_Pandas(a).array()
0 1
0 1 2
1 3 4

Related

What is the difference between a higher-order function and a class?

I was going through the basics of functional programming, and eventually came accross the concept of higher-order functions. I saw an example in this video by Corey Schafer (starts at 11:00), which shows a Python function that can wrap messages in arbitrary HTML tags:
def html_tag(tag):
def wrap_text(msg):
print('<{0}>{1}</{0}>'.format(tag, msg))
return wrap_text
print_h1 = html_tag('h1')
print_h1('Test Headline!')
print_h1('Another Headline!')
print_p = html_tag('p')
print_p('Test Paragraph!')
Output:
<h1>Test Headline!</h1>
<h1>Another Headline!</h1>
<p>Test Paragraph!</p>
I get that it gives you the flexibility of re-using the same function for different purposes (different tags, in this example). But you could achieve the same result using Python classes, too:
class HTML_tag:
def __init__(self, tag):
self.tag = tag
def wrap_text(self, msg):
print('<{0}>{1}</{0}>'.format(self.tag, msg))
print_h1 = HTML_tag('h1')
print_h1.wrap_text('Test Headline!')
print_h1.wrap_text('Another Headline!')
print_p = HTML_tag('p')
print_p.wrap_text('Test Paragraph!')
Output:
<h1>Test Headline!</h1>
<h1>Another Headline!</h1>
<p>Test Paragraph!</p>
The higher-order function approach definitely looks cleaner, but apart from the aesthetics, are there any other reasons I might want to prefer a higher-order function over a class? E.g., regarding aspects like
Performance
Memory
...
Higher order functions take and/or return functions. Let's look at both cases.
Taking a function as a parameter
Here, a HOF is definitely the way to go. The class version amounts to a HOF with extra steps. For the class version, you need to have pre-negotiated the key the function is callable on. It's really a useless wrapper around the meat of what you're trying to accomplish.
HOF
def my_map(fn, arr):
result = []
for a in arr:
result.append(fn(a))
return result
my_map(lambda a: a + 1, [1, 2, 3]) # [2, 3, 4]
Class version
def my_map(inst, arr):
result = []
for a in arr:
result.append(inst.fn(a))
return result
class my_mapper:
def fn(self, a):
return a + 1
my_map(my_mapper(), [1, 2, 3]) # [2, 3, 4]
Returning a function
In both versions here, what we're doing is creating an encapsulation of some value a, and a function that works over it.
I think that a class is generally useful if you want more than one function to be defined over some data, when the encapsulated data can change (you're encoding a state machine), or when you expect operations to be specific to your class (ie. users of your class need to know the operations defined over the class).
I would use a function that returns a function, when what I'm doing amounts to partial application, (I have a function that takes multiple parameters, and I want to preapply some, like 'add'). I would also use functools.partial to do this.
def adder(a):
return lambda b: a + b
class adder_class:
def __init__(self, a):
self.a = a
def add(self, b):
return a + b
Ultimately, whether it's best to use a HOF or a class will become clear from context.

self in Python classes

I know first argument in Python methods will be an instance of this class. So we need use "self" as first argument in methods. But should we also specify attribures (variables) in method starting with "self."?
My method work even if i don't specify self in his attributes:
class Test:
def y(self, x):
c = x + 3
print(c)
t = Test()
t.y(2)
5
and
class Test:
def y(self, x):
self.c = x + 3
print(self.c)
t = Test()
t.y(2)
5
For what i would need specify an attribute in methods like "self.a" instead of just "a"?
In which cases first example will not work but second will? Want to see situation which shows really differences between two of them, because now they behave the same from my point of view.
The reason you do self.attribute_name in a class method is to perform computation on that instances attribute as opposed to using a random variable.For Example
class Car:
def __init__(self,size):
self.size = size
def can_accomodate(self,number_of_people):
return self.size> number_of_people
def change_size(self,new_size):
self.size=new_size
#works but bad practice
def can_accomodate_v2(self,size,number_of_people):
return size> number_of_people
c = Car(5)
print(c.can_accomodate(2))
print(c.can_accomodate_v2(4,2))
In the above example you can see that the can_accomodate use's self.size while can_accomodate_v2 passes the size variable which is bad practice.Both will work but the v2 is a bad practice and should not be used.You can pass argument into a class method not related to the instance/class for example "number_of_people" in can_accomodate funtion.
Hope this helps.

Non-iterable Object After \__init__ Array in Python Class

Say I would like to create a python class that behave as array of another class. While the __init__ is called, it recognizes itself as an array (iterable); however, when I call it again through some other method, or even call by the index, the object becomes non-iterable. I wonder which part I got it wrong, or perhaps, there's DO and DON'T for python class?
Last but not least, this is an attempt to simplify one object type to another (trying to cast from one class to another). Perhaps the code below will give a better clarification.
The example is below:
Say I have an object FOO
FOO.name = "john"
FOO.records[0].a = 1
FOO.records[0].b = 2
FOO.records[1].a = 4
FOO.records[1].b = 5
And I create a python class
class BAR:
__init__(self, record):
self.a = int(record.a)
self.b = int(record.b)
and another class which would like to store BAR class as array
class BARS:
__init__(self,bars):
self = numpy.array([]) # regardless the array type whether python native or Numpy it does not work
for item in bars:
self = numpy.append(self, BAR(item))
so what I would expect this code to perform would be that if I call
A = BARS(FOO.records)
I would get an iterable A. But this does not work, though if I call SELF in BARS __init__, it would see SELF as iterable object.
If one should not expect python class to behave in this manner, at least I hope you could help pointing me out, what would be the alternative logical and pythonic way to achieve it.
Perhaps answering my own question after a hint from comment above would be good.
It turns out that assining self in class as itself is a DON'T (silly me trying to get a shortcut).
To achieve an iterable class, one would require __iter__ method alongside with __next__, and __getitem__ to fulfill (maybe some others methods as well, but let's stick to these three for now).
So, the code above should look like this
class BARS:
def __init__(self, records):
self.records = [] # Use list for simplicity
for record in records:
self.records.append(BAR(record))
def __iter__(self):
self.n = 0
return self
def __next__(self):
if self.n < len(self.records):
result = self.records[self.n]
self.n += 1
return result
else:
raise StopIteration
def __getitem__(self, key):
return self.records[key]
Eventually, this will yield a iteration and index accessible object.

Instance from another instance in Python [duplicate]

This question already has answers here:
Copy constructor in python?
(8 answers)
Closed 6 years ago.
In C++, the constructor of a class allows an instance to be constructed from another instance. e.g.
C::C(const C & c) {
bala...;
cc = c.cc;
}
In Python, what is the similar way of doing this? How may I use
c1 = C()
c2 = C(c1)
?
We don't mention the type while defining a variable in python. For example: if x=4, you can always set x to something else, x="shasha". No problem at all.
Note we can not overload a method in python.
Coming back to your question:
Assuming that you understand the python memory management and the difference between a reference and an actual value, You may use deepcopy feature in python:
import copy
class A(object):
def __init__(self):
self.a=10
x = A()
y= copy.deepcopy(x)
x.a=15
print(y.a) # prints 10.
Note that you can always copy one object into another using = operator like y = x but it wont actually copy anything. Now both the references y and x will actually be pointing to the same instance. i.e. if you change anything using one, it will automatically be reflected into the other one:
class A(object):
def __init__(self):
self.a=10
x = A()
y = x
x.a=15
print(y.a) # prints 15.
You can also create a dummy constructor as mentioned in following example:
class A:
def __init__(self):
self.a=10
def dummy_constructor(self):
temp = A()
temp.a = (self.a + 20 - 5 )*100/10
return temp
x=A()
y=x.dummy_constructor()
print(y.a) #250
print(x.a) #10

Add to custom class in Python

I would like to be able to add to a custom class in the style of:
x=myclass("Something", 7)
x + 3
7, of course, corresponds with an inner property that I'd like to increment by adding to it.
The class holds a number that refers to a location in a list. This might seem like something that can be done by a normal integer, but I need it to act as a separate type. This is all done to emulate an old game language. The class is its 'variable' class, and the value of the variable is stored in the aforementioned list. Apparently, on older version of the game, arrays were faked by doing math on the variable object instance to grab a different variable. So I'm trying to emulate that.
If you want to support addition for class instances, you need to define an __add__() method on your class:
class MyClass(object):
def __init__(self, x):
self.x = x
def __add__(self, other):
return self.x + other
Example:
>>> a = MyClass(7)
>>> a + 3
10
To also support 3 + a, define the __radd__() method.
If you want to be able to update the x attribute of MyClass instances using
a += 3
you can define __iadd__().
If you want class instances to behave like integers with some additional methods and attributes, you should simply derive from int.
What you're looking to do is operator overloading. You can do this in python new style classes by overloading the __add__ method like so:
>>> class Test(object):
... def __init__(self): self.prop = 3
... def __add__(self, x):
... return self.prop + x
...
>>> Test() + 4
7
>>>

Categories