The value you are defining is not an instance field for your class, its more like a static field. But python doesn't mind if you access this field from instances. So, even if you access this field from instances, it is not a different list for each instance. Basically, you are appending to the same list every time the method is called.
You'll have to do this
class A(object):
def __init__(self):
self.value = []
def method(self, new_value):
self.value.append(new_value)
Now you have a different list created for each instance.
EDIT: Let me try to explain what happens when you use a str.
class A(object):
self.value = 'str'
def method(self):
self.value += '1'
That last line in the previous code is the same as this:
self.value = self.value + '1'
Now, this makes it abit easier to see what's going on. First, python gets the
value from self.value. Since there is no instance field defined yet on self,
this will give 'str'. Add '1' to that and sets it to the instance field
called value. This is like
self.value = 'str1'
which is the same as you'd set an instance field in the __init__ method (in my
first snippet of code).
self.value = []
Does that make it clear?
Answer from sharat87 on Stack OverflowThe value you are defining is not an instance field for your class, its more like a static field. But python doesn't mind if you access this field from instances. So, even if you access this field from instances, it is not a different list for each instance. Basically, you are appending to the same list every time the method is called.
You'll have to do this
class A(object):
def __init__(self):
self.value = []
def method(self, new_value):
self.value.append(new_value)
Now you have a different list created for each instance.
EDIT: Let me try to explain what happens when you use a str.
class A(object):
self.value = 'str'
def method(self):
self.value += '1'
That last line in the previous code is the same as this:
self.value = self.value + '1'
Now, this makes it abit easier to see what's going on. First, python gets the
value from self.value. Since there is no instance field defined yet on self,
this will give 'str'. Add '1' to that and sets it to the instance field
called value. This is like
self.value = 'str1'
which is the same as you'd set an instance field in the __init__ method (in my
first snippet of code).
self.value = []
Does that make it clear?
Define value in __init__(). There is no other way to define instance attributes.
Attributes bound outside a instance method are class attributes and shared by all instances of that class. Hence modifications of the objects bound to class attributes affect all instances of the class, as you've noticed in your example.
I'm trying to create texas holdem with players being class instances with hand as an attribute of type list. When I instantiate a class with no value for hand it says I need instantiate it with a list, and when I try to use self.hand = None it doesn't allow me to append new generated cards to it since it's value type none, what should I do to work around this?
Edit: Now that I'm home I can add my code for people to see
Main Code
from pokerMethods import *
from playerClass import *
def main():
deckNumber = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"]
deckSuit = ["Spades", "Diamonds", "Clubs", "Hearts"]
cardList = []
playerList = []
p1 = Player("Herp", "Derp")
p2 = Player("Herpy", "Derpy")
createCard(p1, deckNumber, deckSuit, cardList)
createCard(p2, deckNumber, deckSuit, cardList)
print(p1.hand)
print(p2.hand)
main()Class Code:
class Player:
def __init__(self, fname: str, lname: str, hand = [], money = 0):
self.fname = fname
self.lname = lname
self.hand = hand
self.money = money
def addCard(self, newCard):
self.hand.insert(0,newCard)
def removeMoney(self, bet):
self.money -= bet
def addMoney(self, bet):
self.money += betMethod Code
import random
def createCard(player, deckNumber, deckSuit, cardList,):
cardPlayer = deckNumber[random.randint(0, len(deckNumber)-1)] + " " +
deckSuit[random.randint(0, len(deckSuit)-1)]
if cardPlayer not in cardList:
cardList.append(cardPlayer)
player.addCard(cardPlayer)added hand = [] and then did self.hand = hand because when I did just self.hand = [] it gave me the error
Traceback (most recent call last):
File "Pythons Test Shit\Test'.py", line 17, in <module>
main()
File "Pythons Test Shit\Test'.py", line 9, in main
p1 = Player("Herp", "Derp")
TypeError: __init__() missing 1 required positional argument: 'hand'
and with hand = [] in the initializer
Player 1: ['2 Hearts', '1 Clubs']
Player 2: ['2 Hearts', '1 Clubs']
both instances are having their lists edited
edit 2:
re-read the comments and saw u/Binary101010 's comment, sorry for not trying that before doing all my edits and stuff but thank you so much, it worked
Videos
You demonstrate a fundamental misunderstanding.
You never created an instance of SimpleClass at all, because you didn't call it.
for count in xrange(4):
x = SimpleClass()
x.attr = count
simplelist.append(x)
Or, if you let the class take parameters, instead, you can use a list comprehension.
simplelist = [SimpleClass(count) for count in xrange(4)]
A list comprehension can be used to fill a list with separate instances of a class, like so:
instancelist = [MyClass() for i in range(29)]
This avoids the problem with multiplying a list of one element with *, which re-uses the same object.
Since Python 3.5 you are able to do this:
https://docs.python.org/3/library/typing.html
self.arrayOfGhosts: list[Ghost] = []
You also can import List and use the following code:
from typing import List
self.arrayOfGhosts: List[Ghost] = []
Python is a dynamic language so there is no concept of array of type.
You create an empty generic list with:
self.arrayOfGhosts = []
You don't care about the capacity of the list as it's dynamically allocated as well.
It's up to you to fill it with as many Ghost instances as you wish with:
self.arrayOfGhosts.append(Ghost())
The above is really enough, however:
If you really want to enforce this list to accept only Ghost and inheriting classes instances, you can create a custom list type like this:
class GhostList(list):
def __init__(self, iterable=None):
"""Override initializer which can accept iterable"""
super(GhostList, self).__init__()
if iterable:
for item in iterable:
self.append(item)
def append(self, item):
if isinstance(item, Ghost):
super(GhostList, self).append(item)
else:
raise ValueError('Ghosts allowed only')
def insert(self, index, item):
if isinstance(item, Ghost):
super(GhostList, self).insert(index, item)
else:
raise ValueError('Ghosts allowed only')
def __add__(self, item):
if isinstance(item, Ghost):
super(GhostList, self).__add__(item)
else:
raise ValueError('Ghosts allowed only')
def __iadd__(self, item):
if isinstance(item, Ghost):
super(GhostList, self).__iadd__(item)
else:
raise ValueError('Ghosts allowed only')
Then for two-dimensional list you use this class like:
self.arrayOfGhosts = []
self.arrayOfGhosts.append(GhostList())
self.arrayOfGhosts[0].append(Ghost())
I've spent a long time searching on how to do this and can't seem to crack it.
I have a series of objects that inherit from a parent object. The main differences between the subclasses of the parent are different model types for an ML project.
I have a couple of datasets I'd like to pass to init and run each of the models, for example, something like:
class Dtree(Supervised_Learning):
def __init__(self, features, labels):
super().__init__(features, labels)
#other model init from sklearn here
Models = [Dtree(), KNN(), Boosting(), SVM()]
def run_model(model, features, labels)
mdl = model(features, labels)
mdl.train()
mdl.plot()
for model in Models:
run_model(model)
I've tried just passing the name with out the () and then calling the class().__init___() methods with the features and labels vars, but I can't figure out how to pass the variable for self.
Anyway to do this? Or do I need to just code a function that explicitly inits all of the model objects?
Sure, you can play with the default argument value:
class AB:
def __init__(self, data=None):
self.data = data if data is not None else []
def __add__(self, other):
return AB(self.data + [other.arg])
Other notes:
listis a bad variable name as it is shadowing the built-inlistkeyword- remove redundant parentheses after the class name
Demo:
In [1]: ab = AB()
In [2]: ab += A(1)
In [3]: ab += A(2)
In [4]: print(ab.data)
[<__main__.A instance at 0x10afb14d0>, <__main__.A instance at 0x10afa0998>]
I agree that default arguments can (and should) be used for the backing list of your class.
In addition, consider inheriting from collections.abc.Sequence and delegating __getitem__ and __len__ to the backing list. (Add other list-like methods as necessary.) This will make sure your class acts as a well-behaved list-like.
In order to qualify as an Iterable, you must define __iter__ or __getitem__. In order to qualify as a Sequence you must be an Iterable that provides __getitem__ and __len__. It's up to you how much functionality you want to provide, but the Sequence ABC exists for a reason.
It is a very bad idea to use a mutable object as a default value, as you do here:
def __init__(self, lst=[], intg=0):
# ...
Change it to this:
def __init__(self, lst=None, intg=0):
if lst is None:
lst = []
# ...
The reason that your version doesn't work is that the empty list is created just once when the function is defined, not every time the function is called.
In some Python implementations you can see the value of the default values of the function by inspecting the value of func_defaults:
print test.__init__.func_defaults
name_dict[name] = test()
# ...
Output:
([],) Anne 1 [1] ([1],) Leo 1 [1, 2] ([1, 2],) Suzy 1 [1, 2, 3]
The problem lies in this line:
def __init__(self, lst=[], intg=0):
You shouldn't use a list as a default argument. The first time __init__ is called without lst specified the Python interpreter will define an empty list []. Subsequent calls to the function will operate on the same list if lst is not specified, without declaring a new list. This causes weird problems.
You should instead use a default value of None and add a check at the beginning of the function:
def __init__(self, lst=None, intg=0):
if lst is None:
lst = []
See this post for further details. Quoting the post:
Default arguments are evaluated at function definition time, so they're persistent across calls. This has some interesting (and confusing) side effects. An example:
>>> def foo(d=[]):
... d.append('a')
... return d
If you've not tried this before, you probably expect foo to always return
['a']: it should start with an empty list, append 'a' to it, and return. Here's what it actually does:
>>> foo() ['a']
>>> foo() ['a', 'a']
>>> foo() ['a', 'a', 'a']
This is because the default value for
dis allocated when the function is created, not when it's called. Each time the function is called, the value is still hanging around from the last call. This gets even weirder if you throw threads into the mix. If two different threads are executing the function at the same time, and one of them changes a default argument, they both will see the change.Of course, all of this is only true if the default argument's value is a mutable type. If we change
footo be defined as
>>> def foo2(d=0):
... d += 1
... return d
then it will always return 1. (The difference here is that in
foo2, the variabledis being reassigned, while infooits value was being changed.)
In your __init__ function myList is just local variable. Use self.myList in order to make it as a class member. Of course access it in function addSomething() by using self as well: self.myList.append(x). Also you need to change signature of method addSomething from addSomething(cls, x) to addSomething(self, x)
In Python, there is difference between __init__ and __new__. That is, by the time your code hits __init__ it is already "constructed". Only then you can add new "fields" to an instance, like for example self.myList = [] in __init__.
What def addSomething(cls, x) goes, you may named it cls but it is the instance in fact. So you should write self.myList.append() instead.