3sm1r
3sm1r

Reputation: 530

re-defining an existing function as method of a class

I am learning to use classes. I want to create a class with several methods and attributes. In my case, some of the methods might be functions that I have written elsewhere in the code.

Is it a good practice to re-define the functions as methods of the class like in the following example?

def f(x,b):
    return b*x

class Class1:
    def __init__(self,b):
        self.b=8.
    def f(self,x):
        return f(x,self.b) 


instance1=Class1(5)
print instance1.f(7.)

The code returns what it should, but is this the right way to do it or perhaps it is redundant or it might lead to troubles for larger codes?

What is the right way to define methods using function written elsewhere?

Upvotes: 0

Views: 60

Answers (1)

chepner
chepner

Reputation: 531255

Functions...

Consider the following group of functions:

def stack_push(stack, value):
    stack.append(value)

def stack_is_empty(stack):
    return len(stack) == 0

def stack_pop(stack):
    return stack.pop()

Together, they implement a stack in terms of a built-in list. As long as you don't interact with the list directly, the only thing they allow is adding values to one end, removing values from the same end, and testing if the stack is empty:

>>> s = []
>>> stack_push(s, 3)
>>> stack_push(s, 5)
>>> stack_push(s, 10)
>>> if not stack_is_empty(s): stack_pop(s)
10
>>> if not stack_is_empty(s): stack_pop(s)
5
>>> if not stack_is_empty(s): stack_pop(s)
3
>>> if not stack_is_empty(s): stack_pop(s)
>>>

... vs. Methods

Notice that each function takes the same argument: a list being treated as a stack. This is an indication that we can instead write a class the represents a stack, so that we don't need to maintain a list that could potentially be (mis)used outside of these three functions. It also guarantees that we start with an empty list for our new stack.

class Stack:
    def __init__(self):
        self.data = []

    def push(self, value):
        self.data.append(value)

    def is_empty(self):
        return len(self.data) == 0

    def pop(self):
        return self.data.pop()

Now, we don't work with a list that supports all sorts of non-stack operations like indexing, iteration, and mutation at the beginning or middle of the list: we can only push, pop, and test for emptiness.

>>> s = Stack()

Things like s[3], s.insert(2, 9), etc are not allowed.

(Note that we aren't strictly prevented from using s.data directly, but it's considered bad practice to do so unless the class says it is OK to do so in its documentation. In this case, we do not allow that.)

We use these methods much like we used the stack_* functions.

>>> s.push(3)
>>> s.push(5)
>>> s.push(10)
>>> if not s.is_empty(): s.pop()
10
>>> if not s.is_empty(): s.pop()
5
>>> if not s.is_empty(): s.pop()
3
>>> if not s.is_empty(): s.pop()
>>>

The difference is, we cannot "accidentally" use other list methods, because Stack does not expose them.

>>> s.insert(3, 9)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Stack' object has no attribute 'insert'

Finally, note that we don't write our original stack_* functions and use them in the definition of the Stack class: there is no need; we just define the methods explicitly inside the class statement.

# No.
class Stack:
    def push(self, value):
        stack_push(self.data, value)

We also don't continue to use the stack_* functions on an instance of Stack.

#  No no no!
>>> stack_is_empty(s.data)

Upvotes: 2

Related Questions