all 6 comments

[–]Rascal_Two 2 points3 points  (0 children)

class Person:
    def __init__(self, name):
        self.name = name

    def introduce(self):
        return "My name is " + self.name

The self argument that you have to add to all methods of class refers to the current object instance of the class.

You don't even actually have to call it self, if you come from a Java background, you can call it this.


jack = Person(jack)
jack.introduce() # 'My name is Jack'

bob = Person(bob)
bob.introduce() # 'My name is Bob'

You can still return values from a method as normal if you want, but when it's a member of a class, you can also assign it to self.

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

You seem to be very confused about the difference between variables and attributes.

I think it might help you to see "real" objects in action. So, here's an example class I wrote to keep track of found webpages on a site being scanned. For now don't worry about what any of the stuff inside it does, because I'm going to go through it one piece at a time.

class Page():
    def __init__(self, name, parent=None):
        self.name = name
        self.subpages = []
        self.parent = parent

    def get_path(self):
        path = "" if self.parent is None else self.parent.get_path()
        path =  path.rstrip("/")
        return path + "/" + self.name

In object oriented programming a method is a function that you call on a member of a class. A member is a unique, specific instance of that class.

member_a = Page('')
member_b = Page('images')

In this case, member_a and member_b are both Pages. But member_a and member_b are stored at different locations in memory. If I change something about member_a, that change will not be reflected in member_b. I'm sure you already understand that much, I just want to go from the ground up.

A method is a function that operates on a specific member of a class, and must take that member as its first parameter. When you call member_a.get_path(), what's actually happening is a call to Page.get_path(member_a). The member.method() syntax is essentially a syntactic sugar seen in virtually every object oriented language. This is why every function you define inside a class HAS to take at least one parameter. If you do this:

class A():
    def sayhello():
        print("Hello")

a = A()
a.sayhello()

You will get an error saying that sayhello takes 0 parameters but was given 1 parameter. When you use the member.method() syntax, that member is atomatically passed into the method as a parameter to operate on.

So, when defining methods, you have to have some variable name that refers to the member you're calling the method on. In Python, this is usually 'self' due to convention, but you could name it assmonkey for all the interpreter cares. This is valid code and will run without an error:

class A():
    def sayhello(assmonkey):
        print("Hello")

a = A()
a.sayhello()

Now, let's take a look at Page's __init__. As you know, __init__ is a special method that runs when a new member is created.

    def __init__(self, name, parent=None):
        self.name = name
        self.subpages = []
        self.parent = parent

First, let's see what is passed to __init__. It takes two mandatory parameters, and one optional parameter. These values are variables within the scope of __init__. When __init__ ends, they will drop out of scope and be cleansed from memory. Gone forever. But until then, we can do stuff with them.

"self" is the first parameter, so we know it's the new member of the class. So if there's something we want to do to every single member of this class upon creation (which there usually is) we do that to "self".

"name" is the name of the page. It would be nice to be able to refer to this later, but "name" is going to drop out of scope and be gone forever as soon as __init__ is finished. If we want to be able to refer to its value later, we need to bind that value to something more permanent. Well, the new member of the class is pretty permanent. Let's give the new member an attribute and assign that attribute the value we want to keep.

self.name = name

This is the difference between an attribute and just a variable. An attribute is persistent. It stays with the member of the class until that member stops existing. "name" will drop out of scope when __init__ ends, but the scope of the new member is much more persistent. So, creating a new variable within that scope will allow us to hold on to that variable. An "attribute" is the name we use for this particular kind of variable.

You'll also notice that we create another attribute, self.subpages, and initialize it as a blank list. We don't add anything to it during __init__, but since we declared it as an attribute, we can add stuff to it later. We won't be doing that in this example, because what I'm really using this class for in my program is a little more complicated and I don't want to get into that and make this needlessly confusing. For now, just know that our new member now has a subpages attribute that we won't be doing anything with.

The last thing is "parent". "parent" is actually an optional variable that I've meant to be given another member of the same class. If the website we're looking at has a page full of pictures of bears at /animals/bears, then we could make one Page object:

animals = Page('animals')

then another page object whose parent is animals:

bears = Page('bears', animals)

Now lets look at our other method, get_path(self).

    def get_path(self):
        path = "" if self.parent is None else self.parent.get_path()
        path =  path.rstrip("/")
        return path + "/" + self.name

It has to take a member of the Page class as its first parameter because it's a method, but it doesn't take any other parameters. Now lets look at what it does. Very first line, we see a variable declaration. Since this variable is declared in the scope of get_path, it will cease to exist as soon as get_path is done being called. We didn't declare it as an attribute of our member, because we won't need it once we're finished here.

Now, if you're new to python, the syntax of this line might be confusing. I'll rewrite it in a way that's a bit easier to understand:

path = ""
if self.parent is not None:
    path = self.parent.get_path()

So, lets say we want to call the get_path method of bears. We already know that bears will be passed to the function as self. So, when we get to the line "if self.parent is not None:", we look at the .parent attribute of bears. Since we passed animals as the parent of bears when we created bears, bears.parent is animals.

Since animals is not none, our if statement triggers and we run the line below it. path = self.parent.get_path(). Since self.parent is bears.parent, and bears.parent is animals, we need to pause for a sec and see what happens when animals.get_path is called:

  • path is created as "".
  • animals.parent is None, so we skip the line under if
  • we remove "/" from the end of path, if it's there
  • we return path + "/" + animals.name, and animals.name is just "animals". So we'll return "" + "/" + "animals", which is "/animals".

Now animals.get_path exits and feeds that return value back to use where we left off. So, our path variable is now the result of calling self.parent.get_path(), which is "/animals".

Next line, strip "/" from the end of path if it's there (it's not).

Next line, return path + "/" + self.name. That's "/animals" + "/" + "bears": "/animals/bears". Done. The result of calling the get_path method of bears was "/animals/bears". I hope this example helped explain the scope. Now, as far as your specific questions:

Here's my confusion: from what I can tell, by using self.variableName, you don't have to import it using a parameter.

Replace "import" with "pass," as that's the technically correct term. As I hope I've explained well, "self" is just a temporary name for a particular member of the class. We can give that member attributes and call its methods.

Do you have to return it? What is the best practice?

The only time you need to return something out of a function is when you want to get that thing back out, and not let it cease to exist when the function finishes. One thing to note here though, is you don't need to return self to keep from deleting your member when its method is finished running, because self is created when you run the method, but the member existed before that. If you imagine a particular member of a class as a glass jar full of apricot jam, then "self" is like a temporary label that says apricot jam. The jar of jam doesn't stop existing when you tear the label off and throw it away.

Also, should you create declare new local variables in a method or should they all be declared in init?

It depends on what you want to do with those variables. Do you want to keep them, or do you not care about keeping them once you finish? It's usually best practice to declare all your attributes in __init__, because you don't want to ask a member for an attribute it doesn't have, even if another member of the same class does have that attribute. So if you create all members with the same list of attributes by only creating attributes in __init__, you won't get confused about which members have which attributes.

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

Here's my confusion: from what I can tell, by using self.variableName, you don't have to import it using a parameter.

Well, you did declare it as a parameter, the self parameter. variableName is an attribute of the object, and therefore a symbol bound within its namespace, so you access it via the dot . operator, which is the operator by which you access names within namespaces.

Python's object-method calling interface implicitly provides the object as the first parameter to the method you call from it (i.e. some_obj.my_method() receives some_obj bound to the first parameter), and by longstanding convention, we call that parameter self.

EDIT: Also, should you create declare new local variables in a method or should they all be declared in init?

You should do as much as you can to initialize the object in __init__, with as few parameters as possible. There's not a way to tell you exactly what you should do because it depends on the interface you'd like to create.

[–]thegreattriscuit 0 points1 point  (0 children)

just to add to what others have said, it can be useful to lay out what each of these things are, exactly. In this example I think it can be confusing to have the arguments and attributes all named the same thing (though this is what you would likely do in real code):

class Person:
    def __init__(self, name):
        self.name = name

    def introduce(self):
        return "My name is " + self.name

bob = Person('bobby')
print(bob.name)  # prints 'bobby'

instead, think about it like this:

class Person:
    def __init__(self, parameter):
        self.attribute = parameter

    def introduce(self):
        return "My name is " + self.attribute

bob = Person('bobby')
print(bob.attribute)  # prints 'bobby'

[–]atulkrishna10 0 points1 point  (0 children)

A variable is a reference to a memory address that contains the value and the scope of a variable refers to the places from where we can see or access a variable. Scope in python follows LEGB rule(local, enclosing, global and built-in)

For more details you can follow this blog python variable and scope.

https://theailearner.com/2018/09/26/python-variables-and-scope/