Difference between functions and methods in Python - python

I can't seem to understand the difference between methods and functions.
From my knowledge I am aware that methods are functions that are unique to the classes they are implemented in, but for functions can I say that they can be used in general and are not restricted to a certain class. Also, is the indentation of functions vs methods another essential difference? As methods are implemented within classes and functions are outside with the least indentation.

A function is virtually the same as a method only that the latter is bound to a class. In Python, in most cases, the same way you define a function is the same way you define a method. However, to refer to the class it is in, you will at times see the 'self' parameter added to the method as in: def function_name(self):. The indentation works similarly in both cases.
I think the reason why you tend to think that the indentation of a method is deeper than that of a function is because by the time you're writing your method, you are already indented inside the class.

Python, in particular, across most programming languages, really does not differentiate much between functions and methods.
What you declare is true: "methods are functions that are unique to the classes they are implemented in" -
And that makes for the indentation difference: since methods are inside a class, they are indented inside the class statement block.
When you create a simple class, and retrieve a method from it directly, it is actually a function (in python 3 - it worked differently in old Python 2.x):
from types import FunctionType
class A:
def b(self):
pass
print(A.b, type(A.b) is FunctionType)
Prints:
<function A.b at 0x...> True
So, in Python, a "method" is really and literally a function!
It will work differently when retrieved from an instance of the class it is declared on.
[optional complicated paragraph] Then, Python wraps it with what can be described as "a lazy object callable that will insert the instance it is bound to as first argument to the underlying function when called." Once you can make sense of this phrase, you can check "seasoned Pythonista" in your CV.
What it means is simply that "self" will appear "magically" when calling the method, and moreover, one can go back to the original function by looking at the method's .__func__ attribute.
So, given the same class A above:
In [52]: print(A().b, type(A().b), type(A().b) is FunctionType)
<bound method A.b of <__main__.A object at 0x...>> <class 'method'> False
In [53]: print(A().b.__func__, type(A().b.__func__), type(A().b.__func__) is FunctionType)
<function A.b at 0x...> <class 'function'> True
Note that in these examples I instantiate a new object of the class A at each time, by writing A(), while in the first example, I retrieve b directly from the class, without parenthesis: A.b
As I stated in the beggining of the text: be aware that the fact a plain function can be used as a method is particular to Python, and both kinds of objects will likely have some differences in other programming languages. The idea however, will remain the same: a method will always "know" about the instance from where it was retrieved.

Related

Why method accepts class name and name 'object' as an argument?

Consider the following code, I expected it to generate error. But it worked. mydef1(self) should only be invoked with instance of MyClass1 as an argument, but it is accepting MyClass1 as well as rather vague object as instance.
Can someone explain why mydef is accepting class name(MyClass1) and object as argument?
class MyClass1:
def mydef1(self):
return "Hello"
print(MyClass1.mydef1(MyClass1))
print(MyClass1.mydef1(object))
Output
Hello
Hello
There are several parts to the answer to your question because your question signals confusion about a few different aspects of Python.
First, type names are not special in Python. They're just another variable. You can even do something like object = 5 and cause all kinds of confusion.
Secondly, the self parameter is just that, a parameter. When you say MyClass1.mydef1 you're asking for the value of the variable with the name mydef1 inside the variable (that's a module, or class, or something else that defines the __getattr__ method) MyClass1. You get back a function that takes one argument.
If you had done this:
aVar = MyClass1()
aVar.mydef1(object)
it would've failed. When Python gets a method from an instance of a class, the instance's __getattr__ method has special magic to bind the first argument to the same object the method was retrieved from. It then returns the bound method, which now takes one less argument.
I would recommend fiddling around in the interpreter and type in your MyClass1 definition, then type in MyClass1.mydef1 and aVar = MyClass1(); aVar.mydef1 and observe the difference in the results.
If you come from a language like C++ or Java, this can all seem very confusing. But, it's actually a very regular and logical structure. Everything works the same way.
Also, as people have pointed out, names have no type associated with them. The type is associated with the object the name references. So any name can reference any kind of thing. This is also referred to as 'dynamic typing'. Python is dynamically typed in another way as well. You can actually mess around with the internal structure of something and change the type of an object as well. This is fairly deep magic, and I wouldn't suggest doing it until you know what you're doing. And even then you shouldn't do it as it will just confuse everybody else.
Python is dynamically typed, so it doesn't care what gets passed. It only cares that the single required parameter gets an argument as a value. Once inside the function, you never use self, so it doesn't matter what the argument was; you can't misuse what you don't use in the first place.
This question only arises because you are taking the uncommon action of running an instance method as an unbound method with an explicit argument, rather than invoking it on an instance of the class and letting the Python runtime system take care of passing that instance as the first argument to mydef1: MyClass().mydef1() == MyClass.mydef1(MyClass()).
Python is not a statically-typed language, so you can pass to any function any objects of any data types as long as you pass in the right number of parameters, and the self argument in a class method is no different from arguments in any other function.
There is no problem with that whatsoever - self is an object like any other and may be used in any context where object of its type/behavior would be welcome.
Python - Is it okay to pass self to an external function

Does the Python interpreter bind instances to methods or the self parameter?

I am reading a book about Object-Oriented Programming in Python. There is a sentence that I am confused by:
The interpreter automatically binds the instance upon which the method is invoked to the self parameter.
In this sentence what is bound to the instance. the method, or the self parameter?
This is actually not such a bad question and I'm not sure why it got downvoted so quickly...
Even though Python supports object-oriented, I find it to be much closer to functional-programming languages, one of the reasons for that is that functions are invoked "on" objects, not "by" them.
For example: len(obj) where in a "true" object oriented programing language you'd expect to be able to do something like obj.length()
In regards to the self parameter, you're calling obj.method(other_args) but what really happens under the hood is a translation of this call to: method(obj, other_args) you can see that when the method is declared you're doing it with the self variable passed in as the first argument:
class ...
def method(self, other_args):
...
so it's basically all about the "translation" of obj.method(other_args) to method(obj, other_args)

Why are parenthesis optional when defining a class, but mandatory when defining a function?

In Python, defining a function with an empty parameter list requires a set of empty parenthesis. However, defining a class with the default superclass does not require a set of empty parenthesis; rather, those are optional, and appear to be uncommon. Why is it so?
See also: Python class definition syntax.
I think the answer to your question is simply syntax. That is just the way Python is set up, but my take on how it got that way is:
I would think functions came out of mathematics things like:
f(x) = x
So when computer programming languages were being created there seems to have been some logical continuity from analog mathematics into programming languages.
Classes on the other hand are more constructs of Computer Science, and repetitive memory management, so they were not created in such a fashion, but because they have a functional quality to them, they were given similar notation.
For Python, I will use the term method for function as that is the usual lingo...
I understand your argument that both a class and method should be allowed to be defined using a short-cut in the no argument case:
for classes when there is no inheritence
for methods when there are no arguments
One reason I can think of is for consistency across usage and definition. Let's look at some examples:
definition:
def funcA():
return 0
def funcB(arg):
return arg
and you want to call that funciton:
>>> funcA()
>>> functB("an argument")
and
>>> f1 = funcA
>>> f2 = funcB
>>> f1()
>>> f2("another argument")
to pass references and call them.
The syntax of the paranthesis between method declaration is consistent with calling the methods.
You need to put those empty parenthesis otherwise the interpreter will give you a reference to the method, and not actually call it.
So one benefit is it makes your code very clear.
definition:
class classA:
pass
class classB(object):
pass
usage:
# create an instance
my_instance_of_A = classA()
my_instance_of_B = classB()
# pass a reference
my_ref_to_A = classA
my_ref_to_B = classB
# call by reference
new_A = my_ref_to_A()
new_B = my_ref_to_B()
Here there is no change in behavior with regards to whether the class inherits or not, its calling behavior is dictated by what its internal or inherited __init__ method is defined as.
I think the current set up of requiring the empty () makes the code more readable to the untrained eye.
If you really really really want to do what you ask, there is a workaround... you could always do this:
func = lambda: "im a function declared with no arguments, and I didn't use parenthesis =p"
which can be called:
>>> func
<function <lambda> at 0x6ffffef26e0>
>>> func()
"im a function declared with no arguments, and I didn't use parenthesis =p"
But the python holy book says No

Why do functions/methods in python need self as parameter? [duplicate]

This question already has answers here:
What is the purpose of the `self` parameter? Why is it needed?
(26 answers)
Closed 8 years ago.
I can understand why it is needed for local variables, (self.x), but why is is nessecary as parameter in a function? Is there something else you could put there instead of self?
Please explain in as much layman terms as possible, I never had decent programming education.
By default, every function declared in the namespace of a class assumes that its first argument will be a reference to an instance of that class. (Other types of functions are decorated with #classmethod and #staticmethod to change this assumption.) Such functions are called methods. By convention, Python programmers name that first parameter self, but Python doesn't care what you call it. When a method is called, you must provide that reference. For example (with self replaced by foobar to demonstrate that self is not the required name):
class A:
def __init__(foobar):
foobar.x = 5
def somefunc(foobar, y):
print foobar.x + y
a = A()
print A.somefunc(a, 3) # Prints 8
Python provides some syntactic sugar to make the link between an object and a method called on it more obvious, by letting you call a bound method instead of the function itself. That is, a.somefunc(3) and A.somefunc(a, 3) are equivalent. In Python terminology, A.somefunc is an unbound method, since it still needs an instance when it is called:
f = A.somefunc
print f(a, 3)
By contrast, a.somefunc is called a bound method, since you have already provided the instance to use as the first argument:
f = a.somefunc
print f(3)
If you consider it, EVERY programming language does that - or, at least, the most common languages like pascal, c++ or java do). EXCEPT that, in most programming languages, the this keyword is assumed and not passed as a parameter. Consider the function pointers in those languages: they're different than method-pointers.
Pascal:
function(a: Integer): Integer;
vs
function(a: Integer): Integer of object;
The latter considers the self pointer (yes, it's named self but it's an implicit pointer like the this in c++, while the python self is explicit).
C++:
typedef int (*mytype)(int a);
vs
typedef int Anyclass::(*mytype)(int a);
As difference with Pascal, in C++ you must specify the class owning the method. Anyway, this method pointer declaration states the difference between a function expecting a this or not.
But Python takes seriously it's Zen, as quichua people takes seriously their Ama Suway, Ama Llullay, Ama K'ellay:
Explicit is better than implicit.
So, that's why you see explicitly the self parameter (and must write it, of course) for instance methods and #classmethods (althought it's usually called cls there since it's intention is to dynamically know the class and not the instance). Python does not assume a this or self keyword must exist inside the methods (so, the namespace has only true variables - remember you are NOT forced to name them self or cls althought it's usual and expected).
Finally, if you get:
x = AClass.amethod #unbound method
You must call it as
x(aninstance, param, param2, ..., named=param, named2=param2, ...)
While getting it as:
x = anInstance.method #bound method, has `im_self` attribute set to the instance.
must be called as:
x(param, param2, ..., named=param, named2=param2, ...)
Yes, the self is explicit in the parameter list since it's not assumed a keyword or 'backdoor' must exist, but not in the argument list because of syntactic sugar every OOP language has (weird criteria, huh?).
It's how Python's implementation of object oriented programming works -- a method of an instance (a so-called bound method) is called with that instance as its first argument.
Besides variables of the instance (self.x) you can also use it for anything else, e.g. call another method (self.another_method()), pass this instance as a parameter to something else entirely (mod.some_function(3, self)), or use it to call this method in the superclass of this class (return super(ThisClass, self).this_method()).
You can give it an entirely different name as well. Using pony instead of self will work just as well. But you shouldn't, for obvious reasons.
The use of self for the first reference in a method is entirely convention.
You can call it something else, even inconsistently in the same class:
class Foo(object):
def __init__(notself, i):
notself.i=i # note 'notself' instead of 'self'
def __str__(self):
return str(self.i) # back to the convention
f=Foo(22)
print f
But please don't do that. It is confusing to others that may read your code (or yourself when you read it later).

Python functions can be given new attributes from outside the scope?

I didn't know you could do this:
def tom():
print "tom's locals: ", locals()
def dick(z):
print "z.__name__ = ", z.__name__
z.guest = "Harry"
print "z.guest = ", z.guest
print "dick's locals: ", locals()
tom() #>>> tom's locals: {}
#print tom.guest #AttributeError: 'function' object has no attribute 'guest'
print "tom's dir:", dir(tom) # no 'guest' entry
dick( tom) #>>> z.__name__ = tom
#>>> z.guest = Harry
#>>> dick's locals: {'z': <function tom at 0x02819F30>}
tom() #>>> tom's locals: {}
#print dick.guest #AttributeError: 'function' object has no attribute 'guest'
print tom.guest #>>> Harry
print "tom's dir:", dir(tom) # 'guest' entry appears
Function tom() has no locals. Function dick() knows where tom() lives and puts up Harry as 'guest' over at tom()'s place. harry doesn't appear as a local at tom()'s place, but if you ask for tom's guest, harry answers. harry is a new attribute at tom().
UPDATE: From outside tom(), you can say "print dir(tom)" and see the the tom-object's dictionary. (You can do it from inside tom(), too. So tom could find out he had a new lodger, harry, going under the name of 'guest'.)
So, attributes can be added to a function's namespace from outside the function? Is that often done? Is it acceptable practice? Is it recommended in some situations? Is it actually vital at times? (Is it Pythonic?)
UPDATE: Title now says 'attributes'; it used to say 'variables'. Here's a PEP about Function Attributes.
I think you might be conflating the concepts of local variables and function attributes. For more information on Python function attributes, see the SO question Python function attributes - uses and abuses.
#behindthefall, the motivation to give function objects generic assignable attributes (they didn't use to have them) was that, absent such possibilities, real and popular frameworks were abusing what few assignable attributes existed (typically __doc__) to record information about each given function object. So there was clearly a "pent-up demand" for this functionality, so Guido decided to address it directly (adding an optional dict to each function object to record its attributes isn't a big deal -- most function objects don't need it, and it is optional, so the cost is just 4 bytes for a null pointer;-).
Assigning such attributes in arbitrary places would be very bad practice, making the code harder to understand for no real benefit, but they're very useful when used in a controlled way -- for example, a decorator could usefully record all kinds of things about the function being decorated, and the context in which the decoration occurred, as attributes of the wrapper function, allowing trivially-easy introspection of such metadata to occur later at any time, as needed.
As other answers already pointed out, local variables (which are per-instance, not per-function object!) are a completely disjoint namespace from a function object's attributes held in its __dict__.
In python, a namespace is just a dictionary object, mapping variable name as a string (in this case, 'guest') to a value (in this case, 'Harry'). So as long as you have access to an object, and it's mutable, you can change anything about its namespace.
On small projects, it's not a huge problem, and lets you hack things together faster, but incredibly confusing on larger projects, where your data could be modified from anywhere.
There are ways of making attributes of classes "more private", such as Name Mangling.
tom.guest is just a property on the tom function object, it has nothing to do with the scope or locals() inside that function, and nothing to do with that fact that tom is a function, it would work on any object.
I have used this in the past to make a self-contained function with "enums" that go along with it.
Suppose I were implementing a seek() function. The built-in Python one (on file objects) takes an integer to tell it how to operate; yuck, give me an enum please.
def seek(f, offset, whence=0):
return f.seek(offset, whence)
seek.START = 0
seek.RELATIVE = 1
seek.END = 2
f = open(filename)
seek(f, 0, seek.START) # seek to start of file
seek(f, 0, seek.END) # seek to end of file
What do you think, too tricky and weird? I do like how it keeps the "enum" values bundled together with the function; if you import the function from a module, you get its "enum" values as well, automatically.
Python functions are lexically scoped so there is no way to add variables to the function outside of its defined scope.
However, the function still will have access to all parent scopes, if you really wanted to design the system like that (generally considered bad practice though):
>>> def foo():
>>> def bar():
>>> print x
>>> x = 1
>>> bar()
1
Mutating function variables is mostly a bad idea, since functions are assumed to be immutable. The most pythonic way of implementing this behavior is using classes and methods instead.
Python API documentation generation tools, such as pydoc and epydoc, use introspection to determine a function's name and docstring (available as the __name__ and __doc__ attributes). Well-behaved function decorators are expected to preserve these attributes, so such tools continue to work as expected (i.e. decorating a function should preserve the decorated function's documentation). You do this by copying these attributes from the decorated function to the decorator. Take a look at update_wrapper in the functools module:
WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__')
WRAPPER_UPDATES = ('__dict__',)
def update_wrapper(wrapper,
wrapped,
assigned = WRAPPER_ASSIGNMENTS,
updated = WRAPPER_UPDATES):
"""Update a wrapper function to look like the wrapped function
wrapper is the function to be updated
wrapped is the original function
...
"""
for attr in assigned:
setattr(wrapper, attr, getattr(wrapped, attr))
for attr in updated:
getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
...
So, that's at least one example where modifying function attributes is useful and accepted.
It some situations, it can be useful to "annotate" a function by setting an attribute; Django uses this in a few places:
You can set alters_data to True
on model methods that change the
database, preventing them from being
called in templates.
You can set
allow_tags on model methods that
will be displayed in the admin, to
signify that the method returns HTML
content, which shouldn't be
automatically escaped.
As always, use your judgement. If modifying attributes is accepted practice (for example, when writing a decorator), then by all means go ahead. If it's going to be part of a well documented API, it's probably fine too.

Categories

Resources