Python Class __init__ Confirmation - python

Im trying to understand Python Classes. I am a little confused around defining the __init__. If I have 4 functions created all taking various input variables. Do I have to assign each variable in the __init__?
class Thing:
def __init__(self, arguments, name, address, phone_number, other):
self.arguments = arguments
self.name = name
self.address = address
self.phone_number = phone_number
self.other = other
def First(self, name):
print self.name
def Arguments(self, arguments):
print self.arguments
def Address(self, address, phone_number):
print self.address + str(self.phone_number)
def Other(self, other):
print self.other
The above is completely made up so please excuse its pointlessness (arguably its point is to illustrate my question so guess its not pointless).
No doubt im going to get loads of down marks for this question for some reason but I have been reading various books (Learning Python The Hard Way, Python For Beginners) and been reading various tutorials online but none of them actually confirm "You must add every variable in the init function". So any help understanding the __init__ a little better would be appreciated.

Firstly: The __init__() function is special in that it is called for you while creating a new instance of a class. Apart from that, it is a function like any other function, you can even call it manually if you want.
Then: You are free to do what you want with the parameters passed to a function. Often, the parameters passed to __init__() are stored inside the object (self) that is being initialized. Sometimes, they are used in other ways and the result then stored in self, like passing a hostname and using that to create a socket - you then only store the socket. You don't have to do anything with the parameters though, you can ignore them like you do in your First() function, which receives a parameter name which is ignored. Note that this parameter name and the similar attribute self.name are different things!
Notes:
It is uncommon to ignore parameters passed to __init__() though, just as it is uncommon to ignore parameters in general. In some cases, they are "reserved" so they can be used by derived classes but typically they are unintended (as with your First() function, I guess).
Check out PEP8, which is a style guide. Adhering to it will make it easier for others to read your code. E.g. First() should be first() instead.
Upgrade to Python 3. I don't think there's any excuse nowadays to learn Python 2 and littley excuse except maintenance to use it in general.

If the variable is logically connected to the object itself, it's good to set it in constructor:
class Student:
def __init__(self, name):
self.name = name
def print_name(self):
print(self.name)
If the variable is just a temporary parameter to some function, then, well, just pass it just as a parameter:
class Cafeteria:
def __init__(self):
pass # nothing
def process(self, student_name):
print(student_name + " got lunch")
In Python you don't have to declare all possible object attributes like you have to do in C++, C#, Java etc. I think it's still good idea to initialize them in constructor to some null value (None, 0), but it's not necessary. This is just fine:
class Cafeteria:
def set_today_menu(self, menu):
self.menu = menu
def process(self, student_name):
print(student_name + " got " + self.menu)

You don't have to, but to access them they need to set somewhere...you can also set defaults outside of any of the defs in a class if you want.
def Class:
RandomVariable = 5
def __init__(self, val):
self.RandomVariable = val

This method is known as your initializer where you insatiate an object and then add attributes to it. A human object, if we were to create a class, may contain a name, age, and gender attributes all of which are bound to the object using the self pointer in the initializer. Also, an initializer may modify global variables as well. If you wanted to count the number of babies born, you would add to a global counter variable from our fictitious human class initializer.

Related

Function to create a class

I'd like something like below. Obviously it's invalid syntax, but is it possible to do something like this in python
def make_class(name):
class name:
def __init__(self):
pass
return name
make_class("Test")
a = Test()
Defining a local class in your case seems useless. I'd do that if I did want to return it. There some disadvantages when defining local classes:
Readability: either the class is really trivial or the function
inevitably grows quite long and its logic is lost in the class
declaration. Also you have an extra level of indentation which might
hurt readability if you have some nested loops somewhere
Performance: the class will be re-constructed at every function call.
This usually wont take a huge amount of time, but it will cost a bit.
If the function you are running is fast this cost may be significant.
There are also some advantages of defining a local class:
Locality: you are generally pretty sure that the class wont be used
outside the function, in ways you didn't expect
Performance: looking up a local variable is significantly faster then
looking up a global variable. If you create a big number of instances
this might improve performance with respect to using a global class.
However it's really really easy to counter this advantage via default
arguments/local variables.
My suggestion would be to simply define the class globally and, if it should be private, use a name that starts with an underscore, like _MyClass, since this is the convention used to denote private items.
If you want to find a way of creating a new class without using class keyword, Python classes are instances of type classes. You can create a class by doing like this.
Person = type("Person",
(),
{
"sayHello": lambda: "Hello there"})
You have Person class now with sayHello function defined beforehand.
If you really want to create your function, you can do like this. Actually this is a very bad way. You need to ask your question properly? What do you want to achive?
def create_class(class_name, superclass, namespace):
return type(class_name, superclass, namespace)
Person = create_class("Person",
(),
{
"sayHello": lambda: "Hello there"})
You can do something like this as well.
name = 'Operations'
body = """
def __init__(self, x, y):
self.x = x
self.y = y
def mul(self):
return self.x * self.y
"""
bases = ()
namespace = {}
exec(body, globals(), namespace)
Operations = type(name, bases, namespace)
print(Operations) # output: <class '__main__.Operations'>
instance = Operations(2, 5)
print(instance.mul()) # output: 10
You can define a class and define functions and such in its constuctor, no need to create a new class each time.
Below shows how you can pass in a variable value, a function and how that function can use the object's attributes and how it can be called from another function within the class itself. A lot of stuff, but hopefully by exploring it you will learn a lot!
class my_class:
def __init__(self, name, cool_function):
self.name = name
self.my_function = cool_function
def test(self):
self.my_function(self)
def test_function(self):
print(self.name)
print("Hello World!")
a = my_class("Dave", test_function)
a.test()
# Prints "Dave" and "Hello World!"

Why can't I call a method from my Python class?

I am learning Python and currently working with classes. I am trying to make a basic game to help learn it and am having a weird issue with calling methods
from it. I have the main.py file which creates an instance from the class in the Character.py file.
This is the Character.py file:
class Character:
name=""
def __init__(Name):
name=Name
def getName():
return name
This is the main.py file:
from Character import *
player = Character("James")
print(player.getName())
I am not sure what the issue is. This is the error I get:
Traceback (most recent call last):
File "C:\Users\dstei\Documents\Python\It 102\Final Project\Main.py", line
12, in <module>
print(player.getName())
TypeError: getName() takes 0 positional arguments but 1 was given
It is saying I am giving 1 positional argument but I don't see where I gave any. What am I missing?
Since you have a class with instance methods, you need to include the first argument (self by convention) to refer to the current instance. Also, make sure to set the variable as an instance variable by using self, the current instance:
class Character:
def __init__(self, Name): #self is the current instance
self.name=Name #set the variable on the instance so that every instance of Character has a name
def getName(self):
return self.name #refer to the name with the instance
Python internally passes the new instance of a class as the first argument to all the class methods, like this in languages such as Java. The error comes from the fact that Python passes the instance as the first argument internally but your getter is not defined to take an argument.
With the above code, when you call the method upon an instance, the instance is internally passed as the first argument and Python doesn't complain as you specify that it takes an argument, self, and name is set correctly on the instance.
Note: By convention, Python does not use camelCase, but underscores, so your getter should by convention look like this:
def get_name(self):
#...
Also see chepner's answer which explains why getters and setters aren't usually needed. Just get and modify the instance variable by using dot notation:
print(player.name) #get
player.name = "Jeff" #set
As others have mentioned, even instance method must be declared with an extra argument, typically named self (although that is a conventional, not a required, name).
class Character:
def __init__(self, name):
self.name = name
def get_name(self):
return name
However, Python does not have any kind of enforced visibility (such as public or private), so such trivial getters and setters aren't usually written. Documentation about which attributes you are "allowed" to modify are considered sufficient protection.
class Character:
def __init__(self, name):
self.name = name
c = Character("Bob")
print(c.name) # instead of c.get_name()
c.name = "Charlie" # instead of c.set_name("Charlie")
You are forgetting to add the parameter self. self is an object reference to the object itself, therefore, they are same. Python methods are not called in the context of the object itself. self in Python may be used to deal with custom object models or
class Character:
def __init__(self,name):
self.name=name
def getName(self):
return self.name
To see why this parameter is needed, there are so good answers here:
What is the purpose of self?

Python inheritance: when and why __init__

I'm a Python newbie, trying to understand the philosophy/logic behind the inheritance methods. Questions ultimately regards why and when one has to use the __init__ method in a subclass. Example:
It seems a subclass inheriting from a superclass need not have its own constructor (__init__) method. Below, a dog inherits the attributes (name, age) and methods (makenoise) of a mammal. You can even add a method (do_a_trick) Everything works as it ``should", it seems.
However, if I wanted to add a new attribute in the subclass as I attempt to do in the Cats class, I get an error saying "self" is not defined. Yet I used "self" in the definition of the dog class. What's the nature of the difference?
It seems to define Cats as I wish I need to use __init__(self,name) and super()__init__(name). Why the difference?
class Mammals(object):
def __init__(self,name):
self.name = name
print("I am a new-born "+ self.name)
self.age = 0
def makenoise(self):
print(self.name + " says Hello")
class Dogs(Mammals):
def do_a_trick(self):
print(self.name + " can roll over")
class Cats(Mammals):
self.furry = "True" #results in error `self' is not defined
mymammal = Mammals("zebra") #output "I am a new-born zebra"
mymammal.makenoise() #output "zebra says hello"
print(mymmmal.age) #output 0
mydog = Dogs("family pet") #output "I am a new-born family pet"
mydog.makenoise() #output "family pet says hello"
print(mydog.age) # output 0
mydog.do_a_trick() #output "family pet can roll over"
Explicit is better than implicit.
However, you can do below:
class Dogs(Mammals):
def __init__(self):
#add new attribute
self.someattribute = 'value'
Mammals.__init__(self)
or
class Dogs(Mammals):
def __init__(self):
#add new attribute
self.someattribute = 'value'
super(Mammals, self).__init__()
if I wanted to add a new attribute in the subclass as I attempt to do
in the Cats class, I get an error saying "self" is not defined. Yet I
used "self" in the definition of the dog class.
In your superclass, Mammal, you have an __init__ function, which takes an argument that you've chosen* to call self. This argument is in scope when you're in the body of the __init__ function - it's a local variable like any local variable, and it's not possible to refer to it after its containing function terminates.
The function defined on the Dog class, do_a_trick, also takes an argument called self, and it is also local to that function.
What makes these variables special is not their name (you could call them anything you wanted) but the fact that, as the first arguments to instance methods in python, they get a reference to the object on which they're called as their value. (Read that last sentence again a few times, it's the key to understanding this, and you probably won't get it the first time.)
Now, in Cat, you have a line of code which is not in a function at all. Nothing is in scope at this point, including self, which is why this fails. If you were to define a function in Cat that took an argument called self, it would be possible to refer to that argument. If that argument happened to be the first argument to an instance method on Cat, then it would have the value of the instance of Cat on which it had been called. Otherwise, it would have whatever got passed to it.
*you have chosen wisely!
Declarations at the top level of a Python class become class attributes. If you come from a C++ or Java background, this is similar to declaring a static member variable. You cannot assign instance attributes at that level.
The variable self usually refers to a specific instance of a class, the one from which the method has been called. When a method call is made using the syntax inst.method(), the first argument to the function is the object inst on which the method was called. In your case, and usually by convention, that argument is named self within the function body of methods. You can think of self as only being a valid identifier within method bodies then. Your assignment self.furry = True does not take place in a method, so self isn't defined there.
You have basically two options for achieving what you want. The first is to properly define furry as an attribute of the cat class:
class Cat(Mammals):
furry = True
# Rest of Cat implementation ...
or you can set the value of an instance variable furry in the cat constructor:
class Cat(Mammals):
def __init__(self):
super(Mammals, self).__init__(self)
self.furry = True
# Rest of Cat implementation ...
If you're getting into Python I highly recommend to read these two parts of the Python documentation:
Python classes
Python data model special methods (more advanced)
As pointed out in the other answers, the self that you see in the other
functions is actually a parameter. By Python convention, the first parameter in
an instance method is always self.
The class Cats inherits the __init__ function from its base class,
Mammals. You can override __init__, and you can call or not call the base
class implementation.
In case the Cats __init__ wants to call the base implementation, but doesn't want to care about the parameters, you can use Python variable arguments. This is shown in the following code.
Class declaration:
class Cats(Mammals):
def __init__(self, *args):
super().__init__(*args)
self.furry = "True"
See, for example, this Stack Overflow question for something about the star
notation for variable numbers of arguments:
Can a variable number of arguments be passed to a function?
Additional test code:
cat = Cats("cat")
print(vars(cat))
Output:
I am a new-born cat
{'name': 'cat', 'age': 0, 'furry': 'True'}
You can do something like in Chankey's answer by initiating all the variables in the constructor method ie __init__
However you can also do something like this
class Cats(Mammals):
furry = "True"
And then
cat = Cats("Tom")
cat.furry # Returns "True"
The reason you can't use self outside the functions is because self is used explicitly only for instances of the class. If you used it outside, it would lead to ambiguity. If my answer isn't clear please let me know in comments.
The __init__ method runs once on the creation of an instance of a class. So if you want to set an attribute on an instance when it's created, that's where you do it. self is a special keyword that is passed as the first argument to every method, and it refers to the instance itself. __init__ is no different from other methods in this regard.
"What's the nature of the difference": you define the method Dog.do_a_trick, and you receive self as an argument to the method as usual. But in Cat you've unintentionally (perhaps subconsciously!) attempted to work on the class scope -- this is how you'd set a class attribute whose value is identical for all cats:
class Cat(object):
sound = "meow"
It's different so you can have both options available. Sometimes (not all the time, but once in a while) a class attribute is a useful thing to have. All cats have the same sound. But much of the time you'll work with instance attributes -- different cats have different names; when you need that, use __init__.
Suppose you have a class named Person which has a method named get_name defined as :
class Person():
def __init__(self, first_name, last_name):
self.first_name = first_name
self.last_name = last_name
def get_name(self):
return self.first_name + ' ' + self.last_name
And, you create an instance of Person as p1. Now when you call the function get_name() with this instance, it will converts internally
Person.get_name(p1)
So, self is the instance itself.
Without self you can write above code as :
class Person():
first_name = None
last_name = None
def get_name(personobject):
return personobject.first_name + ' ' + personobject.last_name
What I am trying to say is the name self is a convention only.
And for inheritance, if you would like to have extra attributes in your subclass, you need to initiate your super class first and add your parameter as you wanted.
For example, if you want to create a subclass from Person named Boy with new attribute height, the you can define it as:
class Boy(Person):
def __init__(self, first_name, last_name, height):
super(Person, self).__init__(first_name, last_name)
self.height = height

Understanding data encapsulation in Python

I am reading up on how we ensure data encapsulation in python.One of the blog says
"Data Encapsulation means, that we should only be able to access private attributes via getters and setters"
Consider the following snippets from the blog:
class Robot:
def __init__(self, name=None, build_year=None):
self.name = name
self.build_year = build_year
Now, if i create the object of the class as below:
obj1=Robot()
obj1.name('Robo1")
obj1.build_year("1978")
Currently, i can access the attributes directly as i have defined them public(without the __notation)
Now to ensure data encapsulation, i need to define the attributes as privates
using the __ notation and access private attributes via getters and setters.
So the new class definition is as follows:
class Robot:
def __init__(self, name=None, build_year=2000):
self.__name = name
self.__build_year = build_year
def set_name(self, name):
self.__name = name
def get_name(self):
return self.__name
def set_build_year(self, by):
self.__build_year = by
def get_build_year(self):
return self.__build_year
Now i instantiate the class as below:
x = Robot("Marvin", 1979)
x.set_build_year(1993)
This way, i achive data encapsulation as private data members are no longer accessed directly and they can only be accessed via the class methods.
Q1:Why are we doing this? Who are we protecting the code from? Who is outside world?Anyone who has the source code can tweak it as per their requirement, so why at all do we add extra methods(get/set) to modify/tweak the attributes?
Q2:Is the above example considered data encapsulation?
Data encapsulation is slightly more general than access protection. name and build_year are encapsulated by the class Robot regardless of how you define the attributes. Python takes the position that getters and setters that do nothing more than access or assign to the underlying attribute are unnecessary.
Even using the double-underscore prefix is just advisory, and is more concerned with preventing name collisions in subclasses. If you really wanted to get to the __build_year attribute directly, you still could with
# Prefix attribute name with _Robot
x._Robot__build_year = 1993
A better design in Python is to use a property, which causes Python to invoke a defined getter and/or setter whenever an attribute is defined directly. For example:
class Robot(object):
def __init__(self, name, by):
self.name = name
self.build_year = by
#property
def name(self):
return self._name
#name.setter
def name(self, newname):
self._name = newname
#property
def build_year(self):
return self._build_year
#build_year.setter
def build_year(self, newby):
self._build_year = newby
You wouldn't actually define these property functions so simply, but a big benefit is that you can start by allowing direct access to a name attribute, and if you decide later that there should be more logic involved in getting/setting the value and you want to switch to properties, you can do so without affecting existing code. Code like
x = Robot("bob", 1993)
x.build_year = 1993
will work the same whether or not x.build_year = 1993 assigns to build_year directly or if it really triggers a call to the property setter.
About source code: sometimes you supply others with compiled python files that does not present the source, and you don't want people to get in mess with direct attribute assignments.
Now, consider data encapsulation as safe guards, last point before assigning or supplying values:
You may want to validate or process assignments using the sets, to make sure the assignment is valid for your needs or enters to the variable in the right format, (e.g. you want to check that attribute __build_year is higher than 1800, or that the name is a string). Very important in dynamic languages like python where a variable is not declared with a specific type.
Same goes for gets. You might want to return the year as a decimal, but use it as an integer in the class.
Yes, your example is a basic data encapsulation.

Classes in python, how to set an attributes

When I write class in python, most of the time, I am eager to set variables I use, as properties of the object. Is there any rule or general guidelines about which variables should be used as class/instance attribute and which should not?
for example:
class simple(object):
def __init(self):
a=2
b=3
return a*b
class simple(object):
def __init(self):
self.a=2
self.b=3
return a*b
While I completely understand the attributes should be a property of the object. This is simple to understand when the class declaration is simple but as the program goes longer and longer and there are many places where the data exchange between various modules should be done, I get confused on where I should use a/b or self.a/self.b. Is there any guidelines for this?
Where you use self.a you are creating a property, so this can be accessed from outside the class and persists beyond that function. These should be used for storing data about the object.
Where you use a it is a local variable, and only lasts while in the scope of that function, so should be used where you are only using it within the function (as in this case).
Note that __init is misleading, as it looks like __init__ - but isn't the constructor. If you intended them to be the constructor, then it makes no sense to return a value (as the new object is what is returned).
class Person(object):
def __init__(self, name):
# Introduce all instance variables on __init__
self.name = name
self.another = None
def get_name(self):
# get_name has access to the `instance` variable 'name'
return self.name
So if you want a variable to be available on more than one method, make
it an instance variable.
Notice my comment on introducing all instance vars on __init__.
Although the example below is valid python don't do it.
class Person(object):
def __init__(self):
self.a = 0
def foo(self):
self.b = 1 # Whoa, introduced new instance variable
Instead initialize all your instance variables on __init__ and set
them to None if no other value is appropriate for them.
I try to imagine what I want the API of my class to look like prior to implementing it. I think to myself, If I didn't write this class, would I want to read the documentation about what this particular variable does? If reading that documentation would simply waste my time, then it should probably be a local variable.
Occasionally, you need to preserve some information, but you wouldn't necessarily want that to be part of the API, which is when you use the convention of appending an underscore. e.g. self._some_data_that_is_not_part_of_the_api.
The self parameter refers to the object itself. So if you need to use on of the class attributes outside of the class you would it call it as the name of class instance and the attribute name. I don't think there is any guideline on when to use self, it all depends on your need. When you are building a class you should try to think about what you will use the variables you creating for. If you know for sure that you will need that specific attribute in the program you are importing your class, then add self.

Categories