Dictionary keys not present though they are being added - python

This is my code :
class member:
def __init__(self, name):
self.name = name
def get_name(self, name):
self.name = name
def __str__(self):
return self.name
class create_graph:
def __init__(self):
self.some_dict = dict()
def add(self, name):
if name is None:
raise TypeError
print(name not in self.some_dict)
if name not in self.some_dict:
self.some_dict[name] = []
else:
print(str(name) + "is already present")
def link(self, p1, p2):
if p1 in self.some_dict:
self.some_dict[p1].append(p2)
else:
self.some_dict[p1] = [p2]
some_graph = create_graph()
list_person = ['abc', 'xyz', 'mno', 'pqr']
for person in list_person:
some_graph.add(member(person))
print(len(some_graph.some_dict))
for i in range(len(list_person)-1):
some_graph.link(i,i+1)
print(len(some_graph.some_dict))
I am not able to find the error in this code.
When the add function is called, I get the True message indicating it is added. The first print statement prints that the number of keys are 4 but after adding the links, it says the keys are 7.
I want to have just 4 even after adding the link.
Thanks for the help !

Print out the dictionary in question.
print(some_graph.some_dict)
produces
{<__main__.member object at 0x7fe8326abe80>: [], <__main__.member object at 0x7fe8326abeb8>: [], <__main__.member object at 0x7fe8326abe48>: [], <__main__.member object at 0x7fe8326abef0>: []}
The keys of this dictionary are instances of the class member, not the strings in the list list_person.
I you did:
persons_in_graph_dict = {k.name for k in some_graph.some_dict}
for person in list_person:
print(person)
print(person in persons_in_graph_dict)
print()
You would get:
abc
True
xyz
True
mno
True
pqr
True

You can fix the problem by adding a __contains__() method to your CreateGraph class that expects a string argument called name. How to do this and then use it shown in the code below.
Note: I have changed all your class names to the CapitalizedWords-style to conform to the PEP8 coding guidelines (in its Naming Conventions section).
class Member:
def __init__(self, name):
self.name = name
def get_name(self, name):
self.name = name
def __str__(self):
return self.name
class CreateGraph:
def __init__(self):
self.some_dict = dict()
def add(self, name):
if name is None:
raise TypeError
if name not in self.some_dict:
self.some_dict[name] = Member(name)
else:
print("{} is already present".format(name))
def __contains__(self, name): # <-- METHOD ADDED.
return name in self.some_dict
some_graph = CreateGraph()
list_person = ['abc', 'xyz', 'mno', 'pqr']
for person in list_person:
some_graph.add(person)
print("checking these names in list_person:", list_person)
for person in list_person:
if person in some_graph:
print("Present")
else:
print("Not present")
Here's the output:
checking these names in list_person: ['abc', 'xyz', 'mno', 'pqr']
Present
Present
Present
Present

You are storing instances as key. call name()to get the name
try testing like below
for i in some_graph.some_dict:
print ((i.name) in list_person)

Related

Decorator to automate "for arg in self.args" process in class methods

I created a child class that extends a single parameter from the Parent to *args and yields the outputs. However, it looks cleaner with a decorator instead of writing for arg in self.args: yield in multiple methods.
# Parent class
class _greet:
def _hello(self,name):
return "hello " + name
def _hey(self,name):
return "hey " + name
# Child class
class Greet(_greet):
def __init__(self,*names):
self.names = names
def hello(self):
for name in self.names:
yield super()._hello(name)
def hey(self):
for name in self.names:
yield super()._hey(name)
Although, all of my tries generated an error, because the decorator cannot "find" self.args.
Edit:
The idea behind this is to get something like:
class Greet(_greet):
def __init__(self,*names):
self.names = names
#args(names)
def hello(self, var=name):
super()._hello(var)
Firstly, It would be perfect if you could extend your parent-class with desired behaviour.
Secondly, you should think over the meaning of behaviour extending. Functions _hello and hello are fundamentally different. _hello takes one additional arg and returns created output. hello takes no additional args and creates generator. So may be you don't need to create child class? May be you need to create absolutely independent class (or new function)? Also all your functions in _greet doesn't use self arg - may be they should be static (via #staticmethod)?
Thirdly, are you sure that you need exactly decorators? There are something like 10 idioms I know to emulate same behaviour. Some of them have better productivity, some of them require a small amount of code. Some works with multiple inheritance - some do not. The realisation you have presented (if I understand it correctly) looks like adapter pattern (with mistakes).
Here it is adapter solution:
from itertools import repeat
class Greet(object):
age = 666
#staticmethod
def hello(name):
return f'hello {name}'
#staticmethod
def hey(name):
return f'hey {name}'
def say_age(self, name):
return f'{name} is {self.age} years old'
def multiple_greet_adapter(adapter):
return lambda self: map(adapter, repeat(self), self.names)
class MultipleGreet0(Greet):
def __init__(self, *names):
self.names = names
#multiple_greet_adapter
def hello_many(self, name):
return super().hello(name)
hey_many = multiple_greet_adapter(lambda self, name: super().hey(name))
say_age_many = multiple_greet_adapter(lambda self, name: super().say_age(name))
One of the disadvantages of such implementation is that you still have to write many similar code. Also this not as productive as we want.
Approach 1 - less code, but not productive as well:
from functools import partial
class MultipleGreet1(Greet):
def __init__(self, *names):
self.names = names
_corresponding_names = dict(
hello_many = 'hello',
hey_many = 'hey',
say_age_many = 'say_age',
)
def __getattr__(self, attr_name):
try:
single_greet_handler = getattr(super(), self._corresponding_names[attr_name])
except KeyError:
raise AttributeError()
else:
return partial(map, single_greet_handler, self.names)
Approach 2 - the same, but with descriptors:
class ManyGreets(object):
def __init__(self, attr_name):
self._attr_name = attr_name
def __get__(self, owner_inst, owner_cls):
if owner_inst is None:
return self
else:
return partial(map, getattr(super(owner_cls, owner_inst), self._attr_name), owner_inst.names)
class MultipleGreet2(Greet):
def __init__(self, *names):
self.names = names
hello_many = ManyGreets('hello')
hey_many = ManyGreets('hey')
say_age_many = ManyGreets('say_age')
Approach 3 - a good way you can do if MultipleGreet is independent class:
def _create_many_greets(single_greet_handler, method=True):
if method:
return lambda self: map(single_greet_handler, repeat(self), self.names)
else:
return lambda self: map(single_greet_handler, self.names)
class MultipleGreet3(object):
def __init__(self, *names):
self.names = names
age = 123
hello_many = _create_many_greets(Greet.hello, False)
hey_many = _create_many_greets(Greet.hey, False)
say_age_many = _create_many_greets(Greet.say_age)
Approach 4 - the way I recommend if MultipleGreet depends on Greet:
class ManyGreetsCreator(object):
def __init__(self, attr_name):
self._attr_name = attr_name
def __set_name__(self, owner_cls, set_name):
attr_name = self._attr_name
many_greets = lambda s: map(getattr(super(owner_cls, s), attr_name), s.names)
setattr(owner_cls, set_name, many_greets)
class MultipleGreet4(Greet):
def __init__(self, *names):
self.names = names
hello_many = ManyGreetsCreator('hello')
hey_many = ManyGreetsCreator('hey')
say_age_many = ManyGreetsCreator('say_age')
Tests:
>>> mg0 = MultipleGreet0('Nick', 'John')
>>> mg1 = MultipleGreet1('Nick', 'John')
>>> mg2 = MultipleGreet2('Nick', 'John')
>>> mg3 = MultipleGreet3('Nick', 'John')
>>> mg4 = MultipleGreet4('Nick', 'John')
>>> list(mg4.hello_many())
['hello Nick', 'hello John']
>>> list(mg0.hello_many()) == list(mg1.hello_many()) == list(mg2.hello_many()) ==\
list(mg3.hello_many()) == list(mg4.hello_many())
True
>>> list(mg0.say_age_many()) == list(mg1.say_age_many()) == list(mg2.say_age_many()) ==\
list(mg4.say_age_many())
True
>>> list(mg4.say_age_many())
['Nick is 666 years old', 'John is 666 years old']
>>> list(mg3.say_age_many())
['Nick is 123 years old', 'John is 123 years old']
You can read more about descriptors, about __getattr__, about super-class. There are also approaches based on __init_subclass__

Recursive listing of all objects at an arbitrary depth in a data model?

Suppose I have a data structure composed of objects in Python like this:
class Planet:
def __init__(self, name):
self.name = name
self.continents = []
class Continent:
def __init__(self, name):
self.name = name
self.countries = []
class Country:
def __init__(self, name):
self.name = name
self.states = []
class State:
def __init__(self, name):
self.name = name
self.cities = []
class City:
def __init__(self, name):
self.name = name
Now suppose I was to create a function that would be able to take an object and an attribute "path" as parameters and output a list of all the objects that "match" that path. Since I don't really know how to describe very well what I'm thinking of and most likely I'm using the wrong terminology, here is an idea of what it might look like in practice (let's call the function collect, and assume that the data structure is populated with data, with planet being the "root" object of the data structure):
planet = Planet("Earth")
... Code for populating data structure ...
collect(planet, "continents.countries.states.cities")
This function call would return a list of every cities in every state in every country in every continent on the planet. Or if I wanted all the states in the data model I would do:
collect(planet, "continents.countries.states")
Hopefully you get the idea. The problem is that I just have no clue how I would accomplish this. Here's what I've come up with so far:
def collect(obj, attr_path):
attrs = attr_path.split(".")
current_attr = obj
items = []
for attr in attrs:
current_attr = getitem(current_attr, attr)
# Not sure what to do here...
return items
Does anyone know how I can make this work?
If you would like to do a breadth first search:
def bfs(queue, results):
try:
obj, attr_path = queue.pop(0)
except IndexError:
return
if attr_path is None:
results.append(obj)
else:
if '.' in attr_path:
first, rest = attr_path.split('.', maxsplit=1)
else:
first, rest = attr_path, None
children = getattr(obj, first)
for child in children:
queue.append((child, rest))
bfs(queue, results)
def collect(obj, attr_path):
queue = [(obj, attr_path)]
results = []
bfs(queue, results)
return results
Disclaimer: untested

AttributeError 'str' object has no attribute

I am new to python and I get stuck in this error. I want to print names and years of birth of animals in team in an order by the name. Now I am keeping getting printing years and names but without order. I wanted to use lambda but than error occures. Could you help me please?
class Animal:
def __init__(self, name):
self.name = name
class Team:
def __init__(self, name):
self.name = name
self.year_of_birth = year_of_birth
self.members = []
def add_member(self, member):
self.member = member
self.members.append(team.member)
def print_team(team):
list_members= []
for member in team.members:
list_members.append(member.name)
list_members.append(member.year_of_birth)
print('{} ({})'.format(member.name ,member.year_of_birth) )
print (list_members)
for memeber in list_members:
sorted(list_members, key = lambda member: member.name)
print (list_members)
team = Team('Wolves')
team.add_member(Animal('Josh', 2015))
team.add_member(Animal('Quinn', 2016))
team.add_member(Animal('Peter', 2010))
print_team(team)
line , in
sorted(memberlist, key = lambda member: member.name)
AttributeError: 'str' object has no attribute 'name'
Here is another possible solution:
In order to print an object of any class that you created, you must implement the __str__() method or the __repr__() method as an official string representation of your objects. So, here is the modified Animal class:
class Animal:
def __init__(self, name, year_of_birth):
self.name = name
self.year_of_birth = year_of_birth # Added this field because your created Animal objects had it in the example.
def __str__(self):
return self.name + " " + str(self.year_of_birth)
def __repr__(self):
return self.name + " " + str(self.year_of_birth)
Next thing, I simplified your add_member() method because there was no reason for self.member = member:
def add_member(self, member):
self.members.append(member)
Next, I modified your print_team() function like this:
def print_team(team):
list_members= []
for member in team.members:
list_members.append(member)
print("Unsorted: ")
print (list_members)
list_members.sort(key = lambda animal: animal.name)
print("Sorted by name: ")
print (list_members)
You can simply append any object of type Animal in the list_members list. After that, you can sort your list using sort() and then print it. The code below:
team = Team('Wolves',2015)
team.add_member(Animal('Josh',2015))
team.add_member(Animal('Quinn',2145))
team.add_member(Animal('Peter',3000))
print_team(team)
Produces the following result:
Unsorted:
[Josh 2015, Quinn 2145, Peter 3000]
Sorted by name:
[Josh 2015, Peter 3000, Quinn 2145]

How do you use dictionary in class?

I have to put name and student_num in dictionary.
the thing is I don't know how to call dictionary in class? how can i put name and student_num in that format?
class Cool():
def __init__(self, name, student_num, new_dict):
self._key = name
self._value = student_num
self._new_dict = new_dict
def get_name(self):
return self._key
def get_student_number(self):
return self._value
def new_dict(self):
return self._new_dict
# I want to put name and student_name in this format:
# {'name': 'student name'}
def get_dict(self, new_dict):
self._new_dict = {}
self._new_dict[self._key] = self._value
return self._new_dict[self._key]
def __str__(self):
return str(self._new_dict[self._key])
Now when I test this code
>>>a = Cool({}, 'diana', 1234)
>>>print(a)
I get the following error:
File "C:\Program Files (x86)\Wing IDE 101 5.0\src\debug\tserver_sandbox.py", line 20, in str
builtins.TypeError: 'int' object is not subscriptable
You are calling cool incorrectly.
Try:
>>>a = Cool('diana', 1234, {})
Some more suggestions:
class Cool():
def __init__(self, name, student_num, new_dict):
self._key = name
self._value = student_num
self._new_dict = new_dict
def get_name(self):
return self._key
def get_student_number(self):
return self._value
def get_dict(self):
self._new_dict[self._key] = self._value
return self._new_dict[self._key]
def __str__(self):
return str(self._new_dict[self._key])
In order to get print to work (the way the code is currently). You need to first call get_dict.
You could just do the following:
a = Cool('diana', 1234, {})
print(a.get_dict())
or you could edit your code to handle the dict earlier.
To your edited question,
>>>a = Cool({}, 'diana', 1234)
does this:
class Cool():
def __init__(self, name, student_num, new_dict):
self._key = name # name is {}
self._value = student_num # student_num is 'diana'
self._new_dict = new_dict # new_dict is 1234
Then
>>print(a)
does this:
def __str__(self):
return str(self._new_dict[self._key])
=> _new_dict is 1234
=> return 1234[self._key]
=> self._key is {}
=> return 1234[{}]
=> TypeError: 'int' object is not subscriptable
something[] is a subscript, a slice, operation, and Python can't do that with numbers.
It's not clear to me what the whole code should do, but it looks like several times more code than it needs to be. I assume some of this is mandated by the assignment requirements?
Are you creating one "Cool" class per student? Or one "Cool" class for many students? It looks at the moment like that's not certain - if it's one Cool class per student, putting one name in a dictionary and taking it out again doesn't add anything to the class. If it's one Cool class for a lot of students, then get_name makes no sense because there will be many names and get_student_number will need a way to identify which student.

create python factory method

I have the following simple example:
class CatZoo(object):
def __init__(self):
raise NotImplemented
#classmethod
def make_zoo_cat(cls, name, age, gender, location):
cls._names = name
cls._ages = age
cls._genders = gender
cls._location = location
return cls
#classmethod
def make_zoo_cats(cls, names, ages, genders, location):
cls._names = names
cls._ages = ages
cls._genders = genders
cls._location = location
return cls
#property
def location(self):
return self._location
#property
def names(self):
return self._names
def age(self, name):
if name in self._names:
return self._ages[self._names.index(name)]
else:
return None
def gender(self, name):
if name in self._names:
return self._genders[self._names.index(name)]
else:
return None
#property
def meow(self):
return "meow!"
And I am trying to create an object of this class by using the following:
cat_zoo = CatZoo.make_zoo_cat('squeakers', 12, 'M', 'KC')
print "The name is {}".format(cat_zoo.names)
This is just an example, I am just trying to make my factory methods work (make_zoo_cat, make_zoo_cats). The first will be passed one name, age, gender and location where the second would be passed a list of names, ages and genders and one location. If I run this code, I get the following output:
The name is <property object at 0x7fe313b02838>
Thanks,
Remove the NotImplemented initializer and actually create instances of your class, instead of mutating the class itself:
class CatZoo(object):
def __init__(self, name, age, gender, location):
self._names = name
self._ages = age
self._genders = gender
self._location = location
#classmethod
def make_zoo_cat(cls, name, ages, genders, location):
return cls.mak_zoo_cats([name], age, gender, location)
#classmethod
def make_zoo_cats(cls, names, ages, genders, location):
return CatZoo(names, age, gender, location)
#property
def location(self):
return self._location
#property
def names(self):
return self._names
def age(self, name):
if name in self._names:
return self._ages[self._names.index(name)]
else:
return None
def gender(self, name):
if name in self._names:
return self._genders[self._names.index(name)]
else:
return None
#property
def meow(self):
return "meow!"
Note that there was no real difference other than the method name between make_zoo_cat and make_zoo_cats, the difference in argument names doesn't change the functionality here.
Instead, I presumed that ._names should always be a list and that make_zoo_cat (singular) should create a CatZoo with one cat name in it.
Just remember that Python is not Java; you really don't need all those property objects, not where you could just access the attribute directly.
You didn't create any object in your code.
In your make_zoo_cats you return cls, so you still have a class not an instance of this class.
This code will print the yes
if CatZoo.make_zoo_cat('squeakers', 12, 'M', 'KC') == CatZoo:
print 'yes'
You agree than you can't do that, since name its a property it will only exist if you have an instance of that class.
CatZoo.names
to be able to use the property you need on instance of that class
something like that (this will raise in your code):
cat = CatZoo()
cat.names # I can do this now
An other point in your make_zoo_cat you create Class variables, those variables are accessible from the class (no need to have an instance on that class) but are "common" to all.
c1 = CatZoo.make_zoo_cat('squeakers', 12, 'M', 'KC')
print c1._names
print c1._ages
print c1._genders
print c1._location
print '*'*10
print CatZoo._names
print CatZoo._ages
print CatZoo._genders
print CatZoo._location
print '*'*10
c2 = CatZoo.make_zoo_cat('other', 42, 'F', 'FR')
print c2._names
print c2._ages
print c2._genders
print c2._location
print '*'*10
print CatZoo._names
print CatZoo._ages
print CatZoo._genders
print CatZoo._location
print '*'*10
print c1._names
print c1._ages
print c1._genders
print c1._location
the result will be someting like that:
squeakers
12
M
KC
**********
squeakers
12
M
KC
**********
other
42
F
FR
**********
other
42
F
FR
**********
other
42
F
FR
The first two give me the same result, and the last three as well, this is because they are class variables and you always have the same class so modifying one of those variable will affect the other

Categories

Resources