How to declare a member of 'list' in a class? - python

I'm new to python, and it really confused me.
I want to write something like
class line :
points = []
def add(self, point) :
self.points.append(point)
line1 = line()
line2 = line()
line1.add("Some point")
print line2.points
Output: ['Some point']
And the result is like they refers to the same list.
But I do want a object member not a class member.
And I tried, if points is int, or some other simple type it works fine.
Besides, I know if I write
def __init__(self) :
self.points = []
it will also work, but I don't get why they are pointing to same list by default.
ps. I know writing a init will work.
class line :
name = "abc"
def changename(self, name) :
self.name = name
line1 = line()
line2 = line()
line1.changename(123)
print line2.name
But the code above output "abc"
So I don't understand why same kind of declaration act different by type.

Because in this case
class line :
points = []
points is a class member. It belongs to the class, and since line1 and line2 are of the same class, points is the same list.
So you are right in using
def __init__(self) :
self.points = []
instead to create an instance member which is tied to the instance, not the class.
To answer your edit
If you write:
def changename(self, name) :
self.name = name
you override your class member and create a new instance member using self.name = name.
To get back to your first example, it would be like writing:
def add(self, point) :
self.points = self.points + [point]

The first syntax:
class line :
points = []
makes points a class attribute. Therefore, it will be shared between all instances of the class.
To make sure that an attribute belongs to a class, you should do as follows:
class line(object) :
def __init__(self):
self.points = []
i.e., creating the attributed only in __init__, when a new instance is created.

As others have already explained, and as you apparently already understand, the following creates a class attribute, shared by every instance:
class line:
points = []
When you reference this attribute in your code, Python attempts to find it the current scope, and then follows the enclosing scopes. So if you call self.points.append(), you are indeed changing the points class attribute.
When you assign to self.points in your code, you are defining a new points instance attribute. So in your second example when points is a string, the changename function actually creates a new instance attribute when called.
You can try the following:
print line.name # prints the class attribute
print line1.name # prints the class attribute
print line2.name # prints the instance attribute
You will notice that calling changename did create a new instance attribute, while the class attribute is left unchanged. As changename was never called on line1, a reference to its name will resolve to the line class attribute.

Related

Python Inner Class value assignment

I created a inner class within a class and created two instances of the class, but when assigning different values to the inner class properties, it is assigned to both instances. Can anyone help?
Here's the code
class cl1:
def __init__(self,tagName):
self.name = tagName
name1 = 'tagName'
class cl2:
name2 = 'name2'
test1 = cl1('test1')
test2 = cl1('test2')
test1.cl2.name2 = "cl1"
test2.cl2.name2 = 'cl2'
print (test1.cl2.name2)
When running it, the result is
cl2
Why it is not "cl1" as assigned?
The value:
test1.cl2.name2
Is a class attribute for the class cl2. It is associated with that class, not with instances of that class. So even though you're going through references to an instance of the cl1 class to set the value of that attribute, since there is only one of them, the last thing you set it to wins. Your print statements are printing that same single value.
Google "python class vs instance variables" for a number of good looking write-ups of what the differences are between class and instance variables/attributes.
Here's an example that 1) Provides instance vs class atributes just where it seems you wanted them, and 2) renames the classes involved to have uppercase names so it's obvious when you're referencing an instance attribute vs a class attribute.
class Cl1:
class Cl2:
def __init__(self, tagName):
self.name2 = tagName
def __init__(self, tagName1, tagName2):
self.name1 = tagName1
self.cl2 = Cl1.Cl2(tagName2)
test1 = Cl1('test1', 'test2')
test2 = Cl1('test2', 'test2')
test1.cl2.name2 = 'cl1'
test2.cl2.name2 = 'cl2'
print(test1.cl2.name2)
Result:
cl1
Note that because you want to have an instance of the inner class associated with each instance of the outer class, the inner class's constructor has to instantiate an instance of the inner class as part of creating the outer class instance.
Also note the references to self. Instance variables are created inside a classes's constructor (its __init__ function) by referencing them on self, a reference to the instance being created.

adding item to a list within an instance of a class

I have a class with four lists. I intend to create several instances of this class. If I understand correctly, each instance should have its own lists. I wrote a function to add to the list, but I am struggling to make it work. I do need to use input ().with the following code I get the message: type error. line 12. add_item_list_one() missing one required positional argument : self
class My_class:
def __init__(self):
self.list_one = []
self.list_two = []
self.list_three = []
self.list_four = []
def add_item_list_one(self):
self.list_one.append(int(input()))
obj_one = My_class
obj_one.add_item_list_one()
You just assign My_class to obj_one, but do not instanciate it. Add parentheses after My_class to assign an instance of your My_class to obj_one:
obj_one = My_class()
obj_one.add_item_list_one()

Same list address in memory for two different instantiations of the class

I have the following code:
class PWA_Parse():
include = []
def appendInclude(self, element):
self.include.append(element)
def printMemory(self):
print "Class location", hex(id(self)), "List location:", hex(id(self.include))
a = PWA_Parse()
b = PWA_Parse()
a.appendInclude(5)
a.printMemory()
b.printMemory()
The list memory address for both are the same:
Class location 0x29e9788 List location: 0x29e95d0
Class location 0x29e97b0 List location: 0x29e95d0
How could I create a list in the class definition in order to get two separate lists on instantiation?
(Hint: I tried with list() already)
By declaring include as a class variable you are making all instances of this class share the same variable include.
Instead, you should make include an instance variable by initializing it in the __init__() method:
class PWA_Parse():
def __init__(self):
self.include = []
Create a new list in the __init__ method, which automatically gets called after instantiation.

Understanding class-wide variables that should be tied to object instances

Here is a class that assigns a symbol to a player. It should accept a move and add the move to the existing repository of moves of the player.
class Player:
...: positions = []
...: def __init__(self,symbol):
...: self.symbol = symbol
...: def move(self,position):
...: self.position = position
...: self.positions.append(self.position)
My problem is that positions is behaving "globally" in the sense that it is not tied to an object instance, to demonstrate:
>>>a = Player('x')
>>>b = Player('y')
>>>a.move(1)
>>>b.positions
[1]
When you say,
class Player:
positions = []
positions will be a class variable and the same object is used by all the instances of the class. You can confirm by this
player1, player2 = Player(), Player()
print player1.positions is player2.positions # True
print Player.positions is player1.positions # True
If you want to create instance variables (separate positions variable for each and every instance of Player), you can create that in __init__ function. It is a special initializer function, which gets the current actual object as the first parameter. You can create positions variable and attach it to that object like this
class Player:
def __init__(self):
self.positions = []
player1, player2 = Player(), Player()
print player1.positions is player2.positions # False
Here, self refers to the newly constructed object and you are creating a new variable in that object by self.positions and you are initializing it with an empty list by
self.positions = []
So, whenever you create a new instances of Player, self will refer to the new instance created and new variable positions will be created on every instance, which means separate positions variable for each and every instance.
And whenever move is called, you don't have to create a new position variable on self. Instead you can do this
def move(self, position):
self.positions.append(position)
If you are using Python 2.x, its better to use new style classes, like this
class Player(object):
def __init__(self):
self.positions = []
Declare it inside the __init__ method. Anyhting declared outside the __init__ method will be a class attribute and will be shared between all instances of the class.
You don't have to pass it anything:
class Player:
...: def __init__(self,symbol):
...: self.symbol = symbol
self.positions = []
...: def move(self,position):
...: self.position = position
...: self.positions.append(self.position)
Yes you need to make class variable into instance variable, which will bind only to a particular instance.
Currently, positions = [] is class variable which can access from all the instances. So, you better assign to a particular instance variable.
You can do that by define inside __init__(), which will call when you create an instance of that class.
IMO you'd also want to initialize self.position to None in init, lest you hit this error:
p = Player()
p.position
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: Player instance has no attribute 'position'
So you'd want this:
class Player(object): # making it a new-style class
def __init__(self, ...):
...
self.position = None
My problem is that positions is behaving "globally" in the sense that it is not tied to an object instance, to demonstrate
Yes, that is by design of the language. Python follows a rule that is not like some other modern languages with OO support, but is very simple and easily understood: everything you write inside the class block describes a part of the class. (Remember, in Python, everything is an object - including the classes themselves.)
So I do not really understand what the problem is. You clearly already know how to make things that belong to the instances (hint: you are already doing it with symbol). You may just need to re-think your design and be clearer about what you intend to happen.

What is the difference between declaring data attributes inside or outside __init__ [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Python: Difference between class and instance attributes
I'm trying to get my head around OOP in Python and I'm a bit confused when it comes to declare variables within a class. Should I declare them inside of the __init__ procedure or outside it? What's the difference?
The following code works just fine:
# Declaring variables within __init__
class MyClass:
def __init__(self):
country = ""
city = ""
def information(self):
print "Hi! I'm from %s, (%s)"%(self.city,self.country)
me = MyClass()
me.country = "Spain"
me.city = "Barcelona"
me.information()
But declaring the variables outside of the __init__ procedure also works:
# Declaring variables outside of __init__
class MyClass:
country = ""
city = ""
def information(self):
print "Hi! I'm from %s, (%s)"%(self.city,self.country)
me = MyClass()
me.country = "Spain"
me.city = "Barcelona"
me.information()
In your first example you are defining instance attributes. In the second, class attributes.
Class attributes are shared between all instances of that class, where as instance attributes are "owned" by that particular instance.
Difference by example
To understand the differences let's use an example.
We'll define a class with instance attributes:
class MyClassOne:
def __init__(self):
self.country = "Spain"
self.city = "Barcelona"
self.things = []
And one with class attributes:
class MyClassTwo:
country = "Spain"
city = "Barcelona"
things = []
And a function that prints out information about one of these objects:
def information(obj):
print "I'm from {0}, ({1}). I own: {2}".format(
obj.city, obj.country, ','.join(obj.things))
Let's create 2 MyClassOne objects and change one to be Milan, and give Milan "something":
foo1 = MyClassOne()
bar1 = MyClassOne()
foo1.city = "Milan"
foo1.country = "Italy"
foo1.things.append("Something")
When we call information() on the foo1 and bar1 we get the values you'd expect:
>>> information(foo1)
I'm from Milan, (Italy). I own: Something
>>> information(bar1)
I'm from Barcelona, (Spain). I own:
However, if we were to do exactly the same thing, but using instances of MyClassTwo you'll see that the class attributes are shared between instances.
foo2 = MyClassTwo()
bar2 = MyClassTwo()
foo2.city = "Milan"
foo2.country = "Italy"
foo2.things.append("Something")
And then call information()...
>>> information(foo2)
I'm from Milan, (Italy). I own: Something
>>> information(bar2)
I'm from Barcelona, (Spain). I own: Something
So as you can see - things is being shared between the instances. things is a reference to a list that each instance has access to. So if you append to things from any instance that same list will be seen by all other instances.
The reason you don't see this behaviour in the string variables is because you are actually assigning a new variable to an instance. In this case that reference is "owned" by the instance and not shared at the class level. To illustrate let's assign a new list to things for bar2:
bar2.things = []
This results in:
>>> information(foo2)
I'm from Milan, (Italy). I own: Something
>>> information(bar2)
I'm from Barcelona, (Spain). I own:
You're two versions of the code are very different. In python, you have 2 distinct entities: classes and class instances. An instance is what is created when you do:
new_instance = my_class()
You can bind attributes to an instance within __init__ via self (self is the new instance).
class MyClass(object):
def __init__(self):
self.country = "" #every instance will have a `country` attribute initialized to ""
There's nothing terribly special about self and __init__. self is the customary name that is used to represent the instance that gets passed to every method (by default).
a.method() #-> Inside the class where `method` is defined, `a` gets passed in as `self`
The only thing special here is that __init__ gets called when the class is constructed:
a = MyClass() #implicitly calls `__init__`
You can also bind attributes to the class (putting it outside __init__):
class MyClass(object):
country = "" #This attribute is a class attribute.
At any point, you can bind a new attribute to an instance simply by:
my_instance = MyClass()
my_instance.attribute = something
Or a new attribute to a class via:
MyClass.attribute = something
Now it gets interesting. If an instance doesn't have a requested attribute, then python looks at the class for the attribute and returns it (if it is there). So, class attributes are a way for all instances of a class to share a piece of data.
Consider:
def MyClass(object):
cls_attr = []
def __init__(self):
self.inst_attr = []
a = MyClass()
a.inst_attr.append('a added this')
a.cls_attr.append('a added this to class')
b = MyClass()
print (b.inst_attr) # [] <- empty list, changes to `a` don't affect this.
print (b.cls_attr) # ['a added this to class'] <- Stuff added by `a`!
print (a.inst_attr) #['a added this']
When you define a variable in class scope (outside any method), it becomes a class attribute. When you define a value in method scope, it becomes a method local variable. If you assign a value to an attribute of self (or any other label referencing an object), it becomes (or modifies) an instance attribute.

Categories