I have a class. In short, to initialize you have to provide it with some values when you create it:
class Silo:
def __init__(self, id, name, netid, node):
self.id = id
self.name = name
self.node = node
self.netid = netid
I may have more than one Silo and they are dynamically created by way of an sqllite database. For clarity's sake I've forgone the code for the database queries and instead printed the list of silos below in my example:
global siloList # siloList is a list of the Silos objects.
siloList = {} # Initialize
print(silos) # return: [(1, 'Silo 1', 1, 1), (2, 'Silo 2', 1, 4)]
for silo in silos: # loop through silos for each silo
newSilo = Silo(silo[0], silo[1], silo[2], silo[3]) # Create the object from Silo class
siloList[silo[0]] = newSilo # Create a list for each Silo object with the ID as the index
I would like to retrieve the ID for each object based on the name I enter so that I can do things with that class.
For example:
userInput = "Silo 2"
# Obtain "2" based on input "Silo 2" somehow
siloList[2].netid = 9 # Change the netid with siloList[id]
I can't figure out how to obtain the id of that object from a name appearing in it, though.
I found a silo = next((x for x, obj in enumerate(siloList) if obj['name'] == userInput), None) but this gives me an error TypeError: 'int' object is not subscriptable and I can't quite figure out how to make it work for my needs (or even if it will work or whatelse would be better).
Thanks.
You can get a list of the matching Silo's IDs with
matching_silo_ID_list = list(id_ for id_ in siloList if siloList[id_].name == userInput)
If you are sure that the matching list has exactly one element you can then safely use
matching_ID = matching_silo_ID_list[0]
to do
siloList[matching_ID].netid = 9
NB. I guess your siloList = {} is actually a dictionary.
Related
I have a class called Reader which is composed as follows:
class Reader:
name = None
id = None
reader1 = Reader(name='Entrance', id=1)
reader2 = Reader(name='Exit', id=2)
reader3 = Reader(name='Hallway', id=3)
reader4 = Reader(name='Reception', id=4)
Then I have another class called Doorgroup which is composed of Reader objects
class Doorgroup:
name = None
readers = []
doorgroup1 = Doorgroup(name='Borders', readers=[reader1, reader2])
doorgroup2 = Doorgroup(name='Insides', readers=[reader3, reader4])
doorgroup3 = Doorgroup(name='Admin', readers=[reader1, reader2, reader3, reader4])
doorgroup4 = Doorgroup(name='IN', readers=[reader1, reader3, reader4])
doorgroup5 = Doorgroup(name='OUT', readers=[reader2])
Now I have arrays of int representing the reader ids that I want to convert to Doorgroup instances.
For example:
[1, 2] would return [doorgroup1, doorgroup3, doorgroup5] since [1, 2] are a part of readers of doorgroup1, doorgroup3 and [2] is part of doorgroup5
I am new to python but Is there a way where I can implement this logic?
If you want to create Reader objects in that way, then you will need to create an __init__ method with name and id as arguments (as well as self). Similarly for Doorgroup objects. See the below class definitions.
Rather than having reader1, reader2, and so on as separate variables, it would be easier to create a dictionary (a container of key: value pairs) whereby each key denotes the number of the reader (e.g. 1 or 2 or 3 and so on) and value is the corresponding Reader instance. Similarly for Doorgroup objects.
The following code makes the above changes. If I have interpreted your question correctly, it also achieves your desired translation from a list of ids to a list of Doorgroup objects. In my implementation, the final 'list' of Doorgroup objects is in fact a set but you can easily convert it to a list via list() if you need a list instead. The reason that we use a set (a container of distinct hashable objects) is because it guarantees that a Doorgroup object will not be added if it is already inside the set.
Please let me know if there are any questions! I hope that this helps.
class Reader:
def __init__(self, name = None, id = None):
self.name = name
self.id = id
class Doorgroup:
def __init__(self, name = None, readers = []):
self.name = name
self.readers = readers
# create instances of Reader, store them in dict where a key is an id
# and the associated value is a Reader instance
readers = {}
readers[1] = Reader(name = 'Entrance', id = 1)
readers[2] = Reader(name = 'Exit', id = 2)
readers[3] = Reader(name = 'Hallway', id = 3)
readers[4] = Reader(name = 'Reception', id = 4)
# create instances of Doorgroup, store them in a dict where a key is the
# Doorgroup instance number and the value is a Doorgroup instance
dgroups = {}
dgroups[1] = Doorgroup(name = 'Borders', readers = [readers[1], readers[2]])
dgroups[2] = Doorgroup(name = 'Insides', readers = [readers[3], readers[4]])
dgroups[3] = Doorgroup(name = 'Admin', readers = [readers[1], readers[2],
readers[3], readers[4]])
dgroups[4] = Doorgroup(name = 'IN', readers = [readers[1], readers[3],
readers[4]])
dgroups[5] = Doorgroup(name = 'OUT', readers = [readers[2]])
# Convert list of ids to set of Doorgroup instances
ids = [1, 2]
result = set()
for key in dgroups:
for i in ids:
if readers[i] in dgroups[key].readers:
result.add((key, dgroups[key]))
print(result)
Output
{(4, <__main__.Doorgroup object at 0x7fabe5857ca0>), (5, <__main__.Doorgroup object at 0x7fabe5857c40>), (3, <__main__.Doorgroup object at 0x7fabe5857d00>), (1, <__main__.Doorgroup object at 0x7fabe5857dc0>)}
The output set has elements that are 2-tuples whereby the 1st element of each 2-tuple denotes the number of the Doorgroup instance. You didn't ask for this number but I have included it as it is useful for verifying that the code works.
Can anyone please explain how is it possible to get an object which is both hashable and mutable?
I have seen:
Hashable, immutable it does not answer my question
I heard it is possible in python.
Here is some code that shows you the effects of making an object both hashable and mutable. Note that the link you provided does actually answer your question in Andrew Jaffe's answer and the comments under it; I've added some code from this question about hashing in order to help explain.
The default for a python object's hash value is the object ID, which will not change during it's lifetime. A custom value can be provided by __hash__; however, in order to be useful, this must be translated into something that can be hashable, such as an integer or a string.
class test():
use_int = 0
use_name = ""
use_list = []
def __init__(self, use_int:int, use_name:str, use_list:list)->None:
self.use_int = use_int
self.use_name = use_name
self.use_list = use_list
# Compact the attributes into a tuple of int and strings
# Without changing the list into a string, the hash will fail
def __key(self):
return (str(self.use_int), self.use_name,",".join(self.use_list))
# The above step could be done here with a small object like this
def __hash__(self):
return hash(self.__key())
# For fun: try changing this to "__repr__"
def __str__(self):
return ",".join(self.__key())
Let's run this through and see what the outcomes are:
if __name__ == "__main__":
# Initialise our object
test_obj = test(0,"John",["test","more test",])
Anytime we want to look at the hash values, we can use print(test_obj.__hash__()). Try changing the int and seeing if the hash changes. Also, since Python uses a random salt with str hashes to prevent collisions, note also that hashing this way will supply different hash values in different processes.
We can demonstrate that the object is usable as a hashable object by testing if a dictionary will accept the object as a key. Dictionary keys cannot be lists, for example.
test_dict = dict()
test_dict[test_obj] = "first object used as key"
print(test_dict)
Try changing the list within the object, and seeing if it is still acceptable:
test_obj.use_list.append("yet more tests")
test_dict[test_obj] = "second object used as key"
print(test_dict)
What if we need to go back?
del test_obj.use_list[-1]
test_dict[test_obj] = "third object used as key"
print(test_dict)
Note how "first object" has been changed to "third object".
Putting all of this code together:
class test():
use_int = 0
use_name = ""
use_list = []
def __init__(self, use_int:int, use_name:str, use_list:list)->None:
self.use_int = use_int
self.use_name = use_name
self.use_list = use_list
def __key(self):
return (str(self.use_int), self.use_name,",".join(self.use_list))
def __hash__(self):
return hash(self.__key())
def __str__(self):
return ",".join(self.__key())
if __name__ == "__main__":
test_obj = test(0,"John",["test","more test",])
print(test_obj.__hash__())
test_obj.use_int = 1
print(test_obj.__hash__())
test_obj.use_int = 2
print(test_obj.__hash__())
test_dict = dict()
test_dict[test_obj] = "object used as key"
print(test_dict)
test_obj.use_list.append("yet more tests")
test_dict[test_obj] = "second object"
print(test_dict)
del test_obj.use_list[-1]
test_dict[test_obj] = "third object"
print(test_dict)
print(test_obj)
test_obj.use_int = 1
print(test_obj.__hash__())
But what if we need to have a consistent, predictable hash value? __hash() doesn't have to use hash()! It can return other values. This would mean making the process compatible though - otherwise you'll get TypeError: __hash__ method should return an integer.
Try converting the name into an integer:
def __key(self):
name_number = 0
for c in self.use_name:
name_number += ord(c)
return self.use_int + name_number
def __hash__(self):
return self.__key()
def __str__(self):
return str(self.__key())
What happens if you run the dictionary tests in this scenario?
You'll notice that instead of having two entries to the dictionary, you only get one - this is because changing the list does not change the hash value produced by the object.
Outcome of the original random hash dict tests:
{<main.test object at 0x7f05bc1f1fd0>: 'first object'}
{<main.test object at 0x7f05bc1f1fd0>: 'object used as key', <main.test object at 0x7f05bc1f1fd0>: 'second object'}
{<main.test object at 0x7f05bc1f1fd0>: 'third object', <main.test object at 0x7f05bc1f1fd0>: 'second object'}
Outcome of the second fixed hash dict tests:
{<main.test object at 0x7fc7b5510fd0>: 'first object'}
{<main.test object at 0x7fc7b5510fd0>: 'second object'}
{<main.test object at 0x7fc7b5510fd0>: 'third object'}
Apologies if I explain something wrong or use the wrong wording, my programmer vocabulary isn't the best. If anyone understands my problem and has better ways of explaining it feel free to do so. I have a problem similar to a problem here. I want to remove items from a list that occur in another list. But one list will have strings that reference the variable "name" within class objects.
class sword:
name = 'swordName'
class bow:
name = 'bowName'
class axe:
name = 'axeName'
inventory = [sword, bow, sword, axe]
select = ['bowName', 'swordName']
I want to be able to create a list "selectedItems" with the class objects out of inventory based off of the strings in "select" that are equal to the "name" of the class objects. It also needs to work if "inventory" and "select" both have duplicates in them.
Output:
>> inventory = [bow, axe]
>> selectedItems = [bow, sword]
One other thing I would like the program to ignore if there are more "name"s in select than there are corresponding class objects in "inventory", and to ignore if a string in "select" has no corresponding class objects.
For example, if "inventory" is [sword, axe] and "select" is ['bowName', 'non-existent', 'axeName'], the result is that "inventory" is [sword] and "selectedItems" is [axe].
A simple way of explaining this is that select will take from inventory, but if select can't take from inventory nothing happens.
You may make base class with magic methods __eq__ and __hash__ which can allow you to manage comparing your objects as you want:
class BaseItem:
name = None
def __init__(self):
self.__name = self.name
def __eq__(self, other):
return self.__name == other
def __hash__(self):
return id(self.__name)
def __repr__(self):
return f"'{self.__name}'"
class Sword(BaseItem):
name = "swordName"
class Bow(BaseItem):
name = "bowName"
class Axe(BaseItem):
name = "axeName"
inventory = [Sword(), Bow()]
select = ["swordName", "bowName", "axeName", "swordName", "bowName"]
# casting lists into sets and getting difference between them
result = set(inventory) - set(select)
print(result) # output {'swordName', 'bowName'}
eq - actually is unused here but i added that you can compare your objects with strings, lists etc:
Sword() in ["swordName"] # true
Sword() in ["bowName"] # false
Sword() == "swordName" # true
Sword() == "bowName" # false
hash - need to comparing two objects, actually it use for getting difference between two sets
repr - it is not really required method, it needs just for pretty displaying of objects
selectedItems = list()
# make a new list of the names of the objects in the inventory
# inventory and inventory names have the same index for the same item
inventory_names = [x.name for x in inventory]
for s in select:
if s in inventory_names:
index = inventory_names.index(s)
inventory_names.pop(index)
selectedItems.append(inventory.pop(index))
im trying to use pyviz in a jupyter notebook to create some sort of form for others to populate with data.
this data then is to be saved to a nested list on the click of the save button. then you repeat it for every person.
then i need a button to show the current input of the nested list.
can someone point me in the right direction?so far ive got only the input fields, the list is always empty.
# companies at which people are working
company = ['wal', 'even', 'foot']
class Company(param.Parameterized):
# dropdown of company
company = param.ObjectSelector(objects=company)
# name of person
personname = param.String(doc="name")
# age of person
age = param.Number(0)
# save to list button
save_btn = param.Action(lambda self:self.param.trigger('save_btn'),doc="""Save""")
# show list
show_btn = param.Action(lambda self: self.param.trigger('show_btn'),doc="""Show dicitonary""")
# dict which collects all input
all_persons = []
# return content of dict
#param.depends('show_btn')
def show_list(self):
return self.all_persons
# save form content to dict
#param.depends('save_btn')
def save_to_list(self):
temp_list = []
temp_list.append[self.company]
temp_list.append[self.personname]
temp_list.append[self.age]
run = Company()
pn.Column(run.param.company, run.param.personname, run.param.age,run.param.save_btn,run.param.show_btn, run.show_list)
# desired nested list
# [['wal', "bob", "34"], ["foot", "anna", "56"]]
Your code contains a few typos and loose ends. From top to bottom:
all_persons is defined as empty list, but never connected to the temp list created in the method save_to_list()
#param.depends(...) is missing watch=True, e.g. #param.depends('show_btn', watch=True)
show_list() returns self.all_persons, but this variable is never used anywhere
E.g.:
company = ['A', 'B'] # simplified code
class Company(param.Parameterized):
company = param.ObjectSelector(default=company[0], objects=company)
person_name = param.String(doc="name")
age = param.Number(0)
save_btn = param.Action(lambda self: self.save_to_list() , doc="""Save""") # no need for param.trigger here...
print_btn = param.Action(lambda self: self.print_list() , doc="""Print""") # no need for param.trigger here...
all_persons = [] # starts with an empty list
# no need for param.depends, as the method directly is called via param.Action,
def save_to_list(self):
print('safe to list: {}, {}, {}'.format(self.company, self.person_name, self.age))
temp_list = []
temp_list.append(self.company) # use () for append, not []
temp_list.append(self.person_name)
temp_list.append(self.age)
print('temp_list: {}'.format(temp_list))
self.all_persons.append(temp_list)
# no need for param.depends, as the method is directly called via param.Action,
def print_list(self):
print('all_persons: {}'.format(self.all_persons))
run = Company()
layout = pn.Row(run.param)
layout.app() # to see print statements in a notebook, use the server variant via 'app()'
I am trying to figure out how to create variables from a list of tuple and assign them to a class.
I have data organized like this
Name Age Status
John 30 Employed
That I have created a list of tuple like this
employeelist = [(John, 30, Employed), (Steve, 25, Part-Time)]
And a class set up like this
class Employee():
ecount = 0
elist = []
def __init__(self, name, age, emp_status):
self.name = name
self.age = age
self.emp_status = emp_status
self.lookup = {}
Employee.ecount = Employee.ecount+1
Employee.elist.append(self.name)
Using this code I am able to turn the tuple into an instance of the class
for i in range(0, len(employeelist),1):
sublist = [str(n) for n in employeelist[i]]
Employee(sublist[0], sublist[1], sublist[2])
But I am not able to access them. Is there a way to think about setting up the for loop to create a variable from sublist[0] and then create a class out of it (e.g. sublist[0] = Employee(sublist[0], sublist[1], sublist[2]))?
You just need
employees = [Employee(*v) for v in employee_list]
Note that employees and Employee.elist are essentially the same once
each Employee object has been created.