This question already has answers here:
How to avoid having class data shared among instances?
(7 answers)
Closed 6 years ago.
I have a dictionary with 100 Cluster objects, the clusters have several Member objects and I need to add them to the Cluster they belong, my problem is that every Member es being added to every Cluster, and I can't find out why. Here's the code
self.clusters = {}
with open('/tmp/numpy_dumps/kmeansInput.txt.cluster_centres') as f:
for line in f:
cluster = Cluster(line)
self.clusters[cluster.id] = cluster
with open('/tmp/numpy_dumps/kmeansInput.txt.membership') as f:
for line in f:
member = Member(line, self.reps)
self.clusters[member.clusterId].members[member.imageId] = member
for id, cluster in self.clusters.items():
print(cluster)
print(cluster.members)
print('cluster {} has {} members'.format(id, len(cluster.members)))
The output tells me that every cluster has all the members
The problem is very certainly in the Cluster class, that you did'nt post in your snippet. It's a bit of a wild guess but this behaviour is typical of shared attributes, either class attributes or mutable default arguments. If your Cluster class looks like one of the snippets below then looks no further:
# class attributes:
class Cluster(object):
members = {} # this will be shared by all instances
# solution:
class Cluster(object):
def __init__(self):
self.members = {} # this will be per instance
# default mutable argument:
class Cluster(object):
def __init__(self, members={}):
# this one is well known gotcha:
# the default for the `members` arg is eval'd only once
# so all instances created without an explicit
# `members` arg will share the same `members` dict
self.members = members
# solution:
class Cluster(object):
def __init__(self, members=None):
if members is None:
members = {}
self.members = members
Related
I want to initialize variables with default values in a constructor without parameters, like this:
class Persons()
def __init__(self):
self.name="Joan"
p1=Persons()
print(p1.name)
So the result for this, will be "Joan". It will print the name of the person. All good. But how do I do this for multiple objects? I want to print p2.name, p3.name, with different names, just like above.
I got this from an exercise that asks me to "create a constructor without parameters that initializes variables with default data".
Just add name as a parameter:
class Persons()
def __init__(self, name):
self.name = name
p1=Persons(“Joan”)
print(p1.name)
Or
p1.name = “Jim”
If your class can provide a set of possible names, you can create an object from which each call to Person.__init__ gets a different name. For example:
from itertools import cycle
class Person:
names = cycle(["Alice", "Bob", "Carol", "Dan"])
def __init__(self):
self.name = next(Person.names)
Add as many names to the pool as desired.
Names will necessarily repeat once the initial set of names is exhausted and the cycle begins again. With a little more work, we can at least vary the order in which names are produced on each cycle:
from itertools import cycle
import random
class Person:
def shuffled_cycle(names):
while True:
random.shuffle(names)
yield from names
names = shuffled_cycle(["Alice", "Bob", "Carol", "Dan"])
def __init__(self):
self.name = next(Person.names)
Of course, if repeated names aren't a concern, just call random.choice from inside __init__. This is much simpler:
import random
class Person:
def __init__(self):
self.name = random.choice(["Alice", "Bob", "Carol", "Dan"])
Taken to its extreme, just generate random names from a primordial soup of letters:
import random
import string
class Person:
def __init__(self):
k = random.randint(3, 10)
self.name = ''.join(random.sample(string.ascii_lowercase, k)).title()
So the thing is you want to initialize with default values, The code you wrote is correct.Every new instance of "Person" class will initialize with the same default value which is good coding practice.Since it is "default" value thus same. However for the sake of knowledge I have written a small program that can give new names to 5 new instances of class Person however do not use this in production as it is a bad practice but good for learning.
class Person :
#class Variable
name_list=["Jhon","Naruto","James","Smith","Neo"]
instance_count = 0
def __init__(self):
#Resets instance count when instances become greater that 5
instance_count = instance_count%5
#Sets new name
self.name = name_list[instance_count]
#increment instance_count
instance_count = instance_count + 1
In Python, what is standard/commonly used way to reset class to "default state"? For example, before loading something to class you might want to reset existing data.
For example, check out self.clear() method below:
class ConLanguage:
SERIALIZABLE_ATTRIBUTES = \
["roots", "prefixes", "suffixes"...] #list of "important" fields
def __init__(self):
self.roots = []
self.prefixes = []
self.suffixes = []
.....
def clear(self): # <<--- this method
tmp = ConLanguage()
for attr in self.SERIALIZEABLE_ATTRIBUTES:
setattr(self, attr, getattr(tmp))
def loadFromDict(self, inDict):
defaultValues = ConLanguage()
for attr in self.SERIALIZABLE_ATTRIBUTES:
setattr(self, attr, inDict.get(attr, getattr(defaultValues.attr)))
def loads(self, s):
self.loadFromDict(json.loads(s))
This approach seems to do the job, but I wonder if there is another way to do it.
The other question (which does not have accepted answers) seems to cover diferrent problem - it has couple of numerical fields that needs to be all initialized with zero, while in this scenario there are bunch of members that have different default state - dictionaries, arrays, numbers, strings, etc.
So it was less about "how do I iterate through class attributes" and more about: "does python have commonly used paradigm for this particular situation". Accessing attributes by their names using strings doesn't seem quite right.
If you changed SERIAIZABLE_ATTRIBUTES to a dict containing the attributes and their default values, you could avoid initializing a temporary instance to copy the attributes, and initialize the object by calling clear as well, in which case there's no code duplication.
class Foo:
SERIALIZABLE_ATTRIBUTES = {
'belongings' : list,
'net_worth' : float
}
def __init__(self):
self.clear()
def clear(self):
for k, v in SERIALIZABLE_ATTRIBUTES.items():
setattr(self, k, v())
This question already has answers here:
How to avoid having class data shared among instances?
(7 answers)
Closed 9 years ago.
I can create class definition dynamically, like there:
class_name = 'Human'
base_classes = (object,)
attributes = {'name':'',
'books':list(),
'say_hello':lambda self: sys.stdout.write('Hello!')}
Human = type(class_name, base_classes, attributes)
uzumaxy = Human()
uzumaxy.name = 'Maxim'
uzumaxy.books.append('Programming via .NET')
print(uzumaxy.name) # Out: "Maxim"
print(uzumaxy.books) # Out: "['Programming via .NET']"
grandrey = Human()
grandrey.name = 'Andrey'
grandrey.books.append('Programming via python')
print(grandrey.name) # Out: "Andrey"
print(uzumaxy.name) # Out: "Maxim"
print(grandrey.books) # Out: "['Programming via .NET', 'Programming via python']"
print(uzumaxy.books) # Out: "['Programming via .NET', 'Programming via python']", but i'm expecting: "['Programming via .NET']"
Seems, attribute "name" is instance-level, but why attribute "books" is class-level?
How I can dynamically create definition of type with instance-level attributes? Thx for help.
Actually, both name and books are class-level. It's just that strings are immutable, so when you use uzumaxy.name = "Maxim", you're adding a new attribute called name hiding the class name, while for uzumaxy.books.append("Programming via .NET"), you're accessing the existing (class) books and modifying it. Your code is equivalent to this:
class Human(object):
name = ''
books = []
def say_hello(self):
sys.stdout.write("Hello!")
Note the same behavior. Traditionally, we'd fix that by writing Human like this:
class Human(object):
def __init__(self):
self.name = ''
self.books = []
def say_hello(self):
sys.stdout.write("Hello!")
Now each instance has its own name and books. To do this with a dynamically-created type, you do essentially the same thing, giving it an __init__:
def init_human(self):
self.name = ''
self.books = []
attributes = { '__init__': init_human,
'say_hello': lambda self: sys.stdout.write("Hello!") }
They're both class-level. name is simply immutable, so it doesn't look class-level at first glance. Most attempts to modify it will create a new instance-level attribute with the same name.
Just like when writing a class the normal way, you need to create instance attributes in the constructor:
def __init__(self):
self.name = ''
self.books = []
def say_hello(self):
# This prints a newline. The original didn't.
print 'Hello!'
Human = type('Human', (object,), {
'__init__': __init__,
'say_hello': say_hello,
})
It seems their only value is storing attributes. Even then, those can't be changed or the changes get reflected across all instances!
If I have a class here:
# Vertex
class Vertex:
label = str()
ID = int()
outEdges = list()
inEdges = list()
make a new vertex object:
v = Vertex()
and add to v's outedges:
v.outEdges.append(1)
then Vertex.outEdges is no longer an empty list, but also contains 1.
So how should I use python classes? With the copy module? Not at all?
The attributes you are declaring are class-level, and are shared by all instances of the class. You need to use a proper initializer/constructor function if you want those values to be specific to an instance:
# Vertex
class Vertex:
def __init__(self):
self.label = str()
self.ID = int()
self.outEdges = list()
self.inEdges = list()
You can also create methods on classes, not just attributes.
class Vertex:
# def __init__(self): ...
def allEdges(self):
return self.outEdges + self.inEdges
You'll need an instantiation method if you want different instances of the class.
class Vertex:
def __init__(self):
self.label = str()
....
[..] the changes get reflected across all instances!
If you define attributes as you did they will behave as static attributes shared across all instances of given class.
If you want to get instances of given class which do not share attributes values you need to define it like that:
# Vertex
class Vertex:
def __init__(self):
self.label = str()
self.ID = int()
outEdges = list()
inEdges = list()
Hi
I have created a List of Objects. Each object contains a Set. I want to update the set's contents for all the objects in the list. The code that i wrote to accomplish this is
class Player:
name = ""
cardsInHand = set()
hasBid = False
def __init__(self, name):
self.name = name
class CardDeck:
deck = []
def __init__(self):
for i in range(39) :
if i%10>0 and i%10<9 :
self.deck.append(i)
def dealCards(self,player):
cardIndex = 0
for tempPlayer in player:
for j in range(4): # since want to add four elements at a time
tempPlayer.cardsInHand.add(self.deck.pop(cardIndex))
cardIndex = cardIndex +1
in the main method I am calling the above classes with the following code
players = []
players.append(Player("Player0"))
players.append(Player("Player1"))
players.append(Player("Player2"))
players.append(Player("Player3"))
cards.dealCards(players)
The problem is that dealCards method adds the elements to all the sets of objects. Instead of 4 elements in each object's set, I endup with same 16 elements in each objects's set?
I am new to python, am i doing something wrong ?
You're creating class attributes.
class Player:
def __init__(self, name):
self.name = name
self.cardsInHand = set()
self.hasBid = False
You've defined cardsInHand (as well as name and hasBid) to be class variables instead of instance variables; by defining them in the class body, you're defining them to be variables shared by all instances. If you're familiar with Java, they are essentially like static variables. To make them instance variables, you need to declare them in the __init__ method, like so:
def __init__(self, name):
self.name = name
self.hasBid = False
self.cardsInHand = set()