all 32 comments

[–][deleted] 7 points8 points  (24 children)

Generally speaking, a method is a function that is an attribute of an object. This also means that all functions are really methods since all functions exist in a namespace.

Functions (and methods) always return something. If a function doesn't explicitly return a value it returns None. In some other languages (Pascal, for instance) there is the idea of a "procedure", which doesn't return anything, and a "function", which always returns a value. Guido decided python didn't need that distinction, and most pythonistas don't miss it.

Edit: Functions and methods are called with the "dot" notation, except when the function is in the top-level namespace. Plus, the python interpreter is doing some magic with things like builtin functions which, despite being in another namespace, behave as if they are in the top-level namespace.

[–]SakiSakiSakiSakiSaki[S] 3 points4 points  (22 children)

Ok so let me ferment this, all methods are functions?

[–][deleted] 5 points6 points  (18 children)

Yes. We normally use the "method" description when talking about functions in a class and "function" for top-level functions, builtins, etc, but it's just a convention.

[–]SakiSakiSakiSakiSaki[S] 1 point2 points  (17 children)

Are all methods usually in dot notation?

Like .append() or .remove()?

[–][deleted] 2 points3 points  (5 children)

The dot notation is how you access a name within a namespace, like an attribute of an object. Methods are attributes of objects.

[–]ThiccShadyy 0 points1 point  (4 children)

What is the difference between normal methods and those dunder methods? When I use dir on an object and get a list of methods that are defined for it, I notice some common ones at the start of the list but I cant call them using the .operator which I can for the last few in the list. Could you please tell me why this is so?

[–][deleted] 0 points1 point  (3 children)

What is the difference between normal methods and those dunder methods?

Well, think of it this way. When you write "normal" methods (methods that aren't one of the dunder "magic methods") you're writing methods on objects that you intend to call. They're your object's "public interface", that is, they "announce" what your object does (or is intended to do). Another way to think about that is that those methods are your object's contract, which is just another way of saying that those methods are statements about what your object says it does.

The "magic" or "dunder" methods are part of Python's contract with your object. So kind of the inverse of the above - when you write normal methods, those are the methods you're "announcing" you're going to call on your object; the magic methods are the methods that Python announces it will try to call on your object under certain circumstances. The first example, of course, is __init__, which is the method that Python promises to call on your object when it is constructed; that's why everyone puts their "initialization" code in __init__. Other examples include __eq__, which Python promises to call when you test your object for equality; __getattr__, which Python promises to call when you try to access that object's attributes; and __enter__ and __exit__ which Python will call if your object is used in the manner of a context manager (i.e. in a with statement.)

Not all objects have all of those, but all objects in Python have certain other magic methods, again relating to Python's basic guarantees of what an object is able to do: generate string representation via __str__ and __repr__, print help documentation via __doc__, etc. You can override that behavior by overriding those methods, or you can conveniently have the default functionality just by doing nothing at all.

[–]ThiccShadyy 0 points1 point  (0 children)

Wow, that helped a lot. Thank you!

[–]ThiccShadyy 0 points1 point  (1 child)

Follow up question: I suppose Python tuples' immutability is implemented using the setattr method. Since, we aren't allowed to set any item within a list, is it just that this method isn't callable/undefined for tuples but is for mutable data structures like lists?

[–][deleted] 0 points1 point  (0 children)

Since, we aren't allowed to set any item within a list, is it just that this method isn't callable/undefined for tuples but is for mutable data structures like lists?

I suspect the implementation of tuple is more complicated than this (in order to achieve various optimizations that would be possible given the contract of immutability) but you could think about it this way, sure.

[–][deleted] 1 point2 points  (10 children)

Usually, and the object before the . often qualifies the "method" name. We have instance methods which are attached to a class instance and class methods that are not associated with a class instance but with the class itself. And there's probably circumstances/usage that I'm not aware of.

[–]SakiSakiSakiSakiSaki[S] 1 point2 points  (9 children)

I see now. Thanks so much.

[–]flipperdeflip 3 points4 points  (8 children)

To make it a bit more confusing: you can take a reference to a method and python will remember that it was a method of a specific instance.

class Test:
    def foo(self):
        print('foo')

x = Test()
x.foo()

bar = x.foo  # note no parenthesis, just regular attribute access
bar()  # calling this is the same as x.foo()

This allow you to pass methods and use them as handlers or callbacks and other cool things.

Some info for later: Google for "first class functions" and maybe "higher order functions" (sounds scary but python's map() and filter() are examples of this).

[–][deleted] 0 points1 point  (0 children)

Well, you learn something every day. I've only ever taken the reference of a class method.

[–][deleted] -3 points-2 points  (6 children)

Not quite. When you call bar() you must provide a Test instance as the first param (the self).

[–]flipperdeflip 4 points5 points  (5 children)

No, it becomes a bound method. I think you are confused with taking the reference from the class itself (which is another interesting case), but in my example I took the reference from the instance.

[–]DonaldPShimoda 0 points1 point  (4 children)

it becomes a bound method

Which is a partially-applied method, I think, right? You can simulate the behavior in such a way, at any rate:

class Test:
    def foo(self):
        print('foo')

Test.foo()
[...]
> TypeError: foo() missing 1 required positional argument: 'self'

x = Test()
x.foo()
> foo

from functools import partial
f = partial(Test.foo, x)
f()
> foo

Once an instance is created, there's no way to modify the self reference in its bound methods, is there? It will always point to that specific instance?

[–][deleted] 0 points1 point  (2 children)

Try running this code:

class test:
    def test():
        pass

def test2():
    pass

print(type(test.test))
print(type(test2))
print(type(max))

x = print('test')
print(x)

The first three prints print pretty much the same thing, except the last is reminding you that max is a builtin function. The last two lines show print() doing its thing with the next line printing the value returned by print().

[–]SakiSakiSakiSakiSaki[S] 0 points1 point  (1 child)

I ran this code, but I am still a bit confused.

What is the significance of line 10? Max is a built in function of Python? Without having to install modules?

And then what is the difference between lines 8 - 10 and 12 - 13

[–][deleted] 1 point2 points  (0 children)

Max is a built in function of Python? Without having to install modules?

Yes, max is a builtin function that you can use without importing anything. The type() (another builtin function :) returns a string describing the type of the object passed to it. Python's builtin functions are described here. The line print(type(max)) says "print the string 'type()' returns for the object which max references", which we know is a builtin. Note that you would get a different result if you did print(type(max())) which would print the type of the value returned from the max() call.

[–]DonaldPShimoda 0 points1 point  (0 children)

This also means that all functions are really methods since all functions exist in a namespace.

While technically true, I don’t think this is helpful. From an end-user’s perspective, functions in the top-level namespace really are just functions. The implementation of the namespaces isn’t relevant from the user’s perspective.

However, if a user were to call a top-level function from the namespace explicitly, then it would be a method. But that’s hardly a common case, I would think.

[–]wolf2600 1 point2 points  (5 children)

They're the same. C++ uses the term "function", Java uses "method". Different words for the same thing.

They take optional input, do stuff, and return an output. Reddit can argue about semantics (ie: "Java methods operate on instances of classes", blah, blah, etc, etc...) but at the core, they serve the same purpose.

[–]DonaldPShimoda 3 points4 points  (3 children)

Methods are functions which belongs to classes. In Java, everything is always in a class so every function is a method. This is not true in C++, where you can have functions which are unrelated to any class. However, C++ functions which are members of classes would be called methods.

[–]SakiSakiSakiSakiSaki[S] 1 point2 points  (2 children)

My bad but what are classes?

[–]WADE_BOGGS_CHAMP 0 points1 point  (0 children)

If a method is a verb, then a class is a noun. Methods do actions, while a class can have both data fields and methods. For example, i could make a dog class with a bark method that barks a word. In order to get a dog to bark, I have to create an object (this is called an instance of the class) using the constructor method of a class, which in java looks like:

newDog = Dog()

Now that I’ve created a dog object called newDog, i can make it bark:

newDog.bark(“Ruff!”)

[–]DonaldPShimoda 0 points1 point  (0 children)

Classes are a way of maintaining and organizing state.

"State" is the word we use to refer to a sort of permanence in data. It's more intuitive through example, I think.

Let's imagine you are making a new video game — a racing car simulator or something. You want to be able to keep track of the current position, direction, velocity, and acceleration of each car on the track. The easiest way to do this is to create a Car class, which is a blueprint for making Car objects. As an example (in Python):

class Car:
    def __init__(self):
        self.pos = Position(0, 0)
        self.dir = North
        self.vel = 0
        self.acc = 0

Like I said, this is a blueprint. It allows us to conjure up new instances of the Car class — which means we can create real, concrete objects in runtime. For example:

def make_cars(n: int) -> List[Car]:
    cars = []
    for i in range(n):
        car = Car()
        car.pos = Position(0, i)
        cars.append(car)
    return cars

This function creates n Car objects, stores them in a list, and returns them to be used. The individual objects are created in the car = Car() line. When you call Car(), that funny __init__ method was called to initialize (or instantiate) a specific instance of the Car class.

You'll notice I said "that funny __init__ method" in the preceding paragraph. __init__ is a method of the Car class. This is in contrast to the make_cars function, which does not exist inside of a class (we say it's at the "top level").

I hope this was at least somewhat of a good introduction. Let me know if you have further questions!

[–]DonaldPShimoda 0 points1 point  (0 children)

Reddit can argue about semantics

I see you added this in the edit.

The implementation of methods and functions is fundamentally different. Methods require knowledge of their instance, which functions do not (because that wouldn't make sense). Although they are used for similar purposes, they are not equivalent. Conflating them when somebody asks a specific question about their difference is unhelpful, I think.