Make an array class using dictionaries only - python

I am a student who is new to python. I am trying to define an array class below which uses a dictionary as its only member variable. I am assuming that Python implements dictionaries as the only structured type (i.e., there is no array, list, tuple, etc.).
I am facing difficulty in coding such a program.
This is my code:
class array(object):
def __init__(self):
self.dic={}
def __init__(self,diction):
self.dic = {}
for x in diction:
self.dic.append(x)
def __setitem__(self,key,value):
self.dic[key]=value
def __getitem__(self,key):
if key not in self.dic.keys():
raise KeyError
return self.dic[key]
I want the program to work this way:
a = array('a','b','c') #------output------
print(a) # ['a', 'b', 'c']
print(a[1]) # b
a[1] = 'bee'
print(a) # ['a', 'bee', 'c']
a[3] = 'day'
print(a) # ['a', 'bee', 'c', 'day']
print(a[6]) # IndexError exception
Any suggestions, advice. :)

There are quite a few issues with your class definition:
array is already a data structure: better to rename using the proper Python class-naming conventions (MyClass).
You cannot overload function definitions: better to use an unpacking operator (*) to extract all (if any) arguments.
You cannot append to a dictionary: you need to assign to a key.
Your call to print will display a generic class name, since you don't specify a __str__ magic method. Since a dict is unordered, I did some funny business here to make it display as sorted, though I'm sure there's a better way.
No need to raise a KeyError in __getitem__, since this will be raised anyway.
Finally, I corrected your spacing.
Note that I've only implemented the methods necessary to make your test cases work.
class MyArray(object):
def __init__(self, *diction):
self.dic = {}
for i, x in enumerate(diction):
self.dic[i] = x
def __setitem__(self, key, value):
self.dic[key] = value
def __getitem__(self, key):
return self.dic[key]
def __str__(self):
return str([self.dic[i] for i in sorted(self.dic.keys())])

Related

Iterating through list elements and apply to class

I'm looking to establish a list, (list_=[a,b,c]), iterate over the list and apply it to a class (class_). So something like this:
for letter in list_:
x = class_.letter
Is this possible?
Attributes are typically accessed from a class using a dot notation, ex: my_class.attribute_1. This is useful when accessing attributes are hard coded.
But as you point out, this is not useful when needing to dynamically access attributes, as in the case of the list above. The solution in these cases is to access attributes using the getattr() method which takes input of your class and the attribute name. Ex: x = 'attribute_1, getattr(my_class, x) would return the same results as my_class.attribute_1.
💡 It is worth pointing out that attributes can be set in a similar way: my_class.foo = 1 is equivalent to setattr(my_class, 'foo', 1).
Here is a solution where attributes are accessed using a list (from an example class):
class MyClass:
def __init__(self):
self.a = 1
self.b = 2
self.c = 3
# Instantiate your class
my_class = MyClass()
# Create a list of letters
list_1 = ['a', 'b', 'c']
# Get attributes
for letter in list_1:
x = getattr(my_class, letter)
print(f'{letter=}, {x=}')

Python Object as Dict Key using __hash__ for access

Take this super simple class:
class Foo():
def __init__(self, iden):
self.iden = iden
def __hash__(self):
return hash(self.iden)
def __repr__(self):
return str(self.iden)
The goal is to create instances of the class to use as dict keys. If __repr__ is omitted, the keys are the standard object address. With __repr__ a printable representation might be:
f = Foo(1)
g = Foo(2)
d = {f:'a', g:'b'}
print(d)
>>> {1:'a', 2:'b'}
When attempting to access the dict by key though, it does not appear to be immediately obvious how to utilize the __repr__ (or __str__ for that matter) representation as the key.
print(d[1])
>>> KeyError
First thing's first: __repr__() is a red herring. It only affects how the object is displayed. It has nothing to do with what you're trying to do.
If you want to have two separate objects refer to the same slot in a dict, you need two things (reference):
The objects must have the same hash (hash(obj1) == hash(obj2)).
The objects must compare equal (obj1 == obj2).
Your above implementation does the former, but not the latter. You need to add an __eq__() method (which is actually required by the documentation when you define __hash__(), anyway).
class Foo():
def __init__(self, iden):
self.iden = iden
def __hash__(self):
return hash(self.iden)
def __eq__(self, other):
return self.iden == other
>>> d = {Foo(1) : 'a'}
>>> d[1]
'a'

Access the name of a list in a Python print statement

I am running Python 3.6 and Pandas 0.19.2 and want to access the name of a list as follows:
# work on the following two lists
for list in [list1, list2]:
# loop through items in each list
for entry in bucket_set:
# do stuff
# let user know I finished working on list x
print('i just finished working on: '+list)
In the first iteration, this should do stuff and then print out: 'I just finished working on list1'. However: when I run my code, I get the following error:
TypeError: must be str, not list
This makes perfect sense (a list is not a string after all), but is there any way I can get the name of my list as a string ('list1')? I know I can use a dict instead (which is what I'm doing right now), but I'm interested nonetheless.
You could create a class to generally encapsulate a name, but this is a lot of overhead when a dictionary would suffice.
class NamedObject:
def __init__(self, name, obj):
self.name = name
self.obj = obj
def __getattr__(self, attr):
if attr == 'name':
return self.name
else:
return getattr(self.obj, attr)
unnamed_list = [1, 2, 3]
named_list = NamedObject('named_list', unnamed_list)
print(named_list) # [1, 2, 3]
print(named_list.name) # 'named_list'
You can create class or dictionary, or you can iterate your variables.
my_list = [1,2]
my_var_name = [k for k,v in locals().items() if v == my_list][0]
print(my_var_name)
It gives 'my_list'.
Beware that lists values need to be unique!
There are many options, one of them is repr(list)

sorting a list with objects of a class as its items

i have a python list whose items are objects of a class with various attributes such as birthday_score, anniversary_score , baby_score..... I want to to sort the list on the basis of one of these attributes ,say anniversary_score. How do i do it ?
your_list.sort(key = lambda x : x.anniversary_score)
or if the attribute name is a string then you can use :
import operator
your_list.sort(key=operator.attrgetter('anniversary_score'))
attrgetter is handy if you don't know the name of the attribute in advance (eg. maybe it is from a file or a function parameter)
from operator import attrgetter
sorted(my_list, key=attrgetter('anniversary_score'))
Using sorted:
class X():
def __init__(self, a_s):
self.anniversary_score = a_s
li = [X(1), X(2), X(3), X(4)]
sorted(li, key=lambda x: x.anniversary_score)
Generally, the Python Sorting Wiki has just about everything you'll ever need to know about sorting in Python.
One way is to define in your class the __lt__() and __eq__()methods, which will tell Python how one instance of your class should be sorted when compared to another:
class A():
def __init__(self):
self.sortattr = 'anniversary_score' # you can use 'baby_score', 'birthday_score' instead
def __lt__(self, other):
return getattr(self, self.sortattr) < getattr(other, other.sortattr)
def __eq__(self, other):
return getattr(self, self.sortattr) == getattr(other, other.sortattr)
Then, just use sort() as you do for a list of numbers, strings etc:
mylist.sort() # to sort in-place
sorted(mylist) # to return a new sorted list

why does this function not fire in the __init__ method?

class test:
def __init__(self, val):
self.val = val
self.val.lower()
Why doesn't lower() operate on the contents of val in this code?
You probably mean:
self.val = self.val.lower()
Or, more concisely:
class test:
def __init__(self, val):
self.val = val.lower()
To elaborate, lower() doesn't modify the string in place (it can't, since strings are immutable). Instead, it returns a copy of the string, appropriately modified.
The documentation states it pretty clearly:
Return a copy of the string with all the cased characters [4] converted to lowercase.
It is because strings are immutable. You cannot change them in-place.
Thus, you must overwrite the value of the variable like that:
self.val = self.val.lower()
Note: Unless, of course, your self.val is not a string, but rather some mutable object that is changed in-place after calling lower() method. But this is not the case (you can make it the case if you have created the class of self.val, though).
Example of mutable object with lower() method changing it in-place:
>>> class LowerableList(list):
def lower(self):
for i, item in enumerate(self):
self[i] = item.lower()
>>> a = LowerableList(['A', 'a', 'X', 'D'])
>>> a
['A', 'a', 'X', 'D']
>>> a.lower()
>>> a
['a', 'a', 'x', 'd']
Does it help?
In Python there are 2 types of function that leads to this kind of confusion. For example to sort a list you could do:
>>> a.sort()
or
>>> a = sorted(a)
first one sorts in "a", but second sorts "a" and returns new sorted list.

Categories