I was expecting the object items inside other python objects are isolated.
However, the following code shows my expected result is wrong. It seems python uses a central item_list for all Group items. How can I fix this?
class Item:
name = ''
def __init__(self, name):
self.name = name
class Group:
item_list = []
def __init__(self, item):
self.item_list.append(item)
g2 = Group(Item('bb1'))
g2.item_list.append(Item('bb2'))
group_list = []
group_list.append(Group(Item('aaa')))
group_list.append(g2)
group_list.append(Group(Item('ccc')))
print('len = ', len(group_list[-1].item_list))
print('bb2 = ', group_list[1].item_list[1].name)
Result
len = 4
bb2 = bb1
Version
python3 --version
Python 3.5.2
Well, first of all we should make a difference between class attributes and instance attributes. A class attribute (like item_list) belongs to the class itself (in this case "Group"), so it will be accessible by calling Group.item_list. On the other hand, you can define a item_list for every instance of Group by defining self.item_list = [] inside the Group class constructor (__init__).
The Group.item_list array will be unique for the whole class, and thus will be suitable to store things that you want to share across instances (such as g2).
The self.item_list array (that will be different for each instance of Group) will hold values exclusively for the instance itself, so each Group will have its own item_list.
I think you are aiming for the second approach (instance variables) so you should move the definition of item_list = [] inside the class constructor.
The result should look like this:
class Item:
def __init__(self, name):
self.name = name
class Group:
def __init__(self, item):
self.item_list = []
self.item_list.append(item)
item1 = Item("itemName")
group1 = Group(item1)
# This should print an array containing the *item1* instance
print(group1.item_list)
print(group1.item_list[0] == item1)
Variables that are declared outside of the __init__ method (in this case item_list) are shared between all instances of a class (called class variables), which is why your expected result is wrong.
On the other hand, variables inside the __init__ only belong to the given instance of that class.
Your using class variables, which are similar C++ static variables inside classes (i.e. that variable is shared by ALL class instances). You need to put it inside the __init__ (constructor) to make it so each class creates its own version:
class Item:
def __init__(self, name):
self.name = name
class Group:
def __init__(self, item):
self.item_list = []
self.item_list.append(item)
# Though typically you would also have a function like this:
def add_item(self, item):
self.item_list.append(item)
g2 = Group(Item('bb1'))
g2.item_list.append(Item('bb2'))
group_list = []
group_list.append(Group(Item('aaa')))
group_list.append(g2)
group_list.append(Group(Item('ccc')))
print('len = ', len(group_list[-1].item_list))
print('bb2 = ', group_list[1].item_list[1].name)
Instance vs class attributes is covered in other answers. I want to add that you can avoid having shared instance variables by using an immutable type (e.g. tuple) instead of a mutable type (e.g. list) for class attributes. Like that they won't be shared among instances while still allowing you to define class attributes.
class Item:
name = ''
def __init__(self, name):
self.name = name
class Group:
item_list = tuple()
def __init__(self, item):
self.item_list += (item,)
Related
I want a class with static attributes that can be stored using one or more get methods from outside and the stored values can be retrieved using one or more get methods
class contract_data:
contract_header = dict()
contract_item = dict()
contract_schedule = dict()
# #staticmethod
def put_header(line:list,findex:dict):
contract_header[line[findex['VBELN_VA']]] = {'KNKLI':[line[findex['KNKLI']]],
'VTWEG':[line[findex['VTWEG']]],
'SPART':[line[findex['SPART']]],
'VKBUR':[line[findex['VKBUR']]],
'VKGRP':[line[findex['VKGRP']]],
'BSTKD':[line[findex['BSTKD']]]
}
def get_header(keyval:str)->dict:
return contract_header[keyval]
# #staticmethod
def put_item(line: list, findex: dict):
return
#staticmethod
def put_schedule(line: list, findex: dict):
return
I expected that calling contract_data.put_header(line,findex) I could store values in contract_data attribute contract_header. But it fails with runtime error
in put_header:
contract_header[line[findex['VBELN_VA']]] = {'KNKLI':[line[findex['KNKLI']]],
NameError: name 'contract_header' is not defined. Did you mean: 'contract_data'?
I played around with #staticmethod and .self or self. with no success.
I expect the class attributes, the dictionaries can be used within the class but not outside.
Your dicts are not global variables; they're class attributes, and as such need to be accessed from the class. That means your static methods need to be defined as class methods.
class contract_data:
contract_header = dict()
contract_item = dict()
contract_schedule = dict()
#classmethod
def put_header(cls, line: list, findex: dict):
cls.contract_header[line[findex['VBELN_VA']]] = {
k: [line[findex[k]]]
for k in ['KNKLI', 'VTWEB', 'SPART', 'VKBUR', 'VKGRP', 'BSTKD']}
#classmethod
def get_header(cls, keyval: str)->dict:
return cls.contract_header[keyval]
...
I'm new to python and as I was doing an assignment for class, I got stuck using init method.
class Customer(object):
def __init__(self, number, name):
self.name = name
self.number = number
self.orders = []
def addorder(self, order):
self.orders.extend(order)
return self.orders
def __str__(self):
return str(self.orders)
Customer('308','John').addorder((1,2,3,4))
print(Customer('308','John'))
The output is an empty list [].
I want the output to be [1,2,3,4]
What am I doing wrong here?
The issue is that you have two Customer objects. I.e. your print line:
print(Customer('308','John'))
Is creating a new Customer object with a number of '308' and a name of 'John'. It's completely unrelated to the customer on the previous line.
To fix this, you should assign your first object to a variable (think of it like a handle, that lets you access the object), and then print that:
john = Customer('308','John')
john.addorder((1,2,3,4))
print(john)
You're creating two instances of the class
class Customer(object):
def __init__(self, number, name):
self.name = name
self.number = number
self.orders = []
def addorder(self, order):
self.orders.extend(order)
return self.orders
def __str__(self):
return str(self.orders)
customer = Customer('308','John')
customer.addorder((1,2,3,4))
print(customer)
Keep in mind that each time you "call" a class, you instantiate a new object (this is why in many languages other than Python, this actually requires the keyword new). So, in your example, you're instantiating two different objects (that don't share their properties). Instead, you should save them in a variable:
customer = Customer("308", "John")
customer.addorder((1, 2, 3, 4))
print(customer)
I tried to create a data structure in python, where I have an outerClass, innerClass, both would include several variables and the outerClass has a list, storing objects of innerClass instances.
I managed to create the outerClass instances but failed to do it with innerClass instances, especially unable to append them to my innerInstancesList.
I'm quite new to python, not sure exactly if this is the best way implement this structure.
Trying to make something similar:
Outerinstance1
variable1
variable2
Innerinstance1
variable1
variable2
Innerinstance2
variable1
variable2
Innerinstance3
variable1
Outerinstance2
variable1
Is there a better way to implement this in python? My sample code:
class outerClass:
def __init__(self, name):
self.name = name
self.innerInstancesList= []
self.innerClass = self.innerClass()
class innerClass:
def __init__(self):
self.id = 0
self.c = "char"
def init2(self, id, c):
self.id = id
self.c = c
outerInstance = outerClass("Outerinstance1")
print (hex(id(outerInstance)))
for a in range(0,5):
outerInstance.innerClass.init2(1, a)
x = outerInstance.innerClass
print (hex(id(x)))
outerInstance.innerInstancesList.append(x)
It appears that ultimately, you want instances of one class to each track multiple instances of another class. This doesn't actually require that one class be defined inside the other. Let's call them Group and Member; each Group instance can hold multiple instances of Member. A straightforward way to declare and demonstrate such a relationship would look like so:
class Group:
def __init__(self, name):
self.name = name
self.members = []
def __repr__(self):
return f'Group(name={self.name}, members={self.members})'
class Member:
def __init__(self, id, char):
self.id = id
self.char = char
def __repr__(self):
return f'Member(id={self.id}, char={self.char})'
group = Group('Test Group')
print(group)
for a in range(5):
member = Member(a, chr(a + 97))
print(member)
group.members.append(member)
print(group)
I've added __repr__ methods (using f-strings) so that it's easy to tell what objects are which when printed. As you can see, both classes are defined at the top (module) level, then multiple instances of Member are appended to the members list of the Group instance.
This outputs:
Group(name='Test Group', members=[])
Member(id=0, c='a')
Member(id=1, c='b')
Member(id=2, c='c')
Member(id=3, c='d')
Member(id=4, c='e')
Group(name='Test Group', members=[Member(id=0, c='a'), Member(id=1, c='b'), Member(id=2, c='c'), Member(id=3, c='d'), Member(id=4, c='e')])
You could avoid some boilerplate in your class definitions (at least in this simple example) by using dataclasses. They're a handy way to simplify declarations of classes that mostly just store some data fields like this, because they can automatically generate __init__ and __repr__ methods for you, among other things.
from dataclasses import dataclass, field
#dataclass
class Group:
name: str
members: list = field(init=False, default_factory=list)
#dataclass
class Member:
id: int
char: str
# Rest of the code unchanged; will generate the same output
I don't think there is. I think you might be better without inner classes, does not seem to be a great reason to use one. You could just add the class to the constructor, append it, and list it that way.
I would like to store the instance of a class in a container like a list. Other classes/methods should access this instance.
Below is a code snipped which defines a data point.
class dataPoint(object):
def __init__(self, name, typeUUID, value = None):
self.name = name
self.typeUUID = typeUUID
self.value = value
I like to define a method which gives me the reference (no copy constructor, etc.) to this object. Maybe like this:
def getRef(self):
return ???
These references I like to use in different list. The reference I like to use to set properties/call functions of the data point. Below is some pseudocode:
# define a conatiner with datapoints
myContainer = [dataPoint("temperature","double",273.15), dataPoint("power","double",230), dataPoint("errorcode","uint32",666)]
# define interfaces which refers to the datapoints
interface1 = [ref("temperature"), ref("power")]
interface2 = [ref("errorcode"), ]
interface3 = [ref("temperature"), ref("power"), ref("errorcode")]
# set temperature to 300K
ref("temperature") = 300.0
# interfaces
print (interface1[ref("temperature")]) --> 300K
print (interface3[ref("temperature")]) --> 300K
How to do this in Python and how to do this pythonic?
You could put the "instance-container" in the class itself:
class DataPoint:
instances = {}
def __init__(self, name, typeUUID, value=None):
self.name = name
self.typeUUID = typeUUID
self.value = value
self.instances[name] = self
#classmethod
def get(cls, name):
return cls.instances[name]
Then you can use it like this:
>>> d1 = DataPoint("foo", "12345")
>>> d2 = DataPoint("bar", "67890")
>>> DataPoint.get("foo")
<DataPoint object at 0x.........>
A python descriptor that I'm working with is sharing its value across all instances of its owner class. How can I make each instance's descriptor contain its own internal values?
class Desc(object):
def __init__(self, initval=None,name='val'):
self.val = initval
self.name = name
def __get__(self,obj,objtype):
return self.val
def __set__(self,obj,val):
self.val = val
def __delete__(self,obj):
pass
class MyClass(object):
desc = Desc(10,'varx')
if __name__ == "__main__":
c = MyClass()
c.desc = 'max'
d = MyClass()
d.desc = 'sally'
print(c.desc)
print(d.desc)
The output is this, the last call set the value for both objects:
localhost $ python descriptor_testing.py
sally
sally
There is only one descriptor object, stored on the class object, so self is always the same. If you want to store data per-object and access it through the descriptor, you either have to store the data on each object (probably the better idea) or in some data-structure keyed by each object (an idea I don't like as much).
I would save data on the instance object:
class Desc(object):
default_value = 10
def __init__(self, name):
self.name = name
def __get__(self,obj,objtype):
return obj.__dict__.get(self.name, self.default_value)
# alternatively the following; but won't work with shadowing:
#return getattr(obj, self.name, self.default_value)
def __set__(self,obj,val):
obj.__dict__[self.name] = val
# alternatively the following; but won't work with shadowing:
#setattr(obj, self.name, val)
def __delete__(self,obj):
pass
class MyClass(object):
desc = Desc('varx')
In this case, the data will be stored in the obj's 'varx' entry in its __dict__. Because of how data descriptor lookup works though, you can "shadow" the storage location with the descriptor:
class MyClass(object):
varx = Desc('varx')
In this case, when you do the lookup:
MyClass().varx
The descriptor object gets called and can do its lookup, but when the lookup goes like this:
MyClass().__dict__['varx']
The value is returned directly. Thus the descriptor is able to store its data in a 'hidden' place, so to speak.