change the types of superclass - python

I dont understand how changed type from list to dict on constructor in main class if i got to subclass another type - dict.
What is magic is this?
class Adder:
def __init__(self, data = [ ]):
self.data = data
def __add__(self, other):
return "Not Implemented"
class DictAdder(Adder):
def __add__(self, y):
z = {}
for k in self.data.keys(): z[k] = self.data[k]
for k in y.keys(): z[k] = y[k]
return z
y = DictAdder({1:'a', 2:'b'})
l = y + { 3: 'c', 4: 'd'}
print(l)
and answer:
{1: 'a', 2: 'b', 3: 'c', 4: 'd'}
Pycharm show what self.data.keys() dont have attribute keys() and this must be try, because data is a list, but this is work

data defaults to a list, but Python isn't a statically typed language so it doesn't have to be. data is just a name. Just because we have this:
class Adder:
def __init__(self, data = [ ]):
self.data = data
Doesn't limit our usage. I can still do:
i = Adder(4)
s = Adder('hello')
d = Adder({1: 2})
o = Adder(SomeOtherType())
As long as whatever uses Adder.data uses it correctly for the type it happens to be. Since in this case, DictAdder interprets data as a dict, and it is, that works fine. Of course, if you tried to use a DictAdder constructed with a non-dict, you would get an error.

Related

Issue with getting reading dictionary connected to object

I'm working on project and got a problem. When I tried to get the element of dictionary I got key error. Although when I doesn't use variable numer the program works. Even when variable = 2 The program doesn't work. Code:
from typing import Sequence
class Lot:
def __init__(self, numer_lotu, id_samolotu,
czas_lotu, trasa, bramka,
cenaek, cenabiz, cenapr,
nrwolek = None, nrwolbiz= None, nrwolpr = None):
self.numer_lotu = numer_lotu
self.id_samolotu = id_samolotu
self.czas_lotu = czas_lotu
self.trasa = trasa
self.bramka = bramka
self.cenaek = cenaek
self.cenabiz = cenabiz
self.cenapr = cenapr
self.nrwolek = nrwolek
self.nrwolbiz = nrwolbiz
self.nrwolpr = nrwolpr
def get_numer_lotu(self):
return self.numer_lotu
def get_id_samolotu(self):
return self.id_samolotu
def get_czas_lotu(self):
return self.czas_lotu
def get_trasa(self):
return self.trasa
def get_bramka(self):
return self.bramka
def get_cenaek(self):
return self.cenaek
def get_cenabiz(self):
return self.cenabiz
def get_cenapr(self):
return self.cenapr
def get_wolnemiejscaek(self):
return self.nrwolek
def get_wolnemiejscabiz(self):
return self.nrwolbiz
def get_wolnemiejscapr(self):
return self.nrwolpr
class DatabaseofLoty():
def __init__(self, list_of_samolot : Sequence[Lot] = ()):
self.list_of_samoloty = {lot.get_numer_lotu() : lot
for lot in list_of_samolot}
print(self.list_of_samoloty)
self.list_of_samolot = list_of_samolot
def get_list(self):
return self.list_of_samolot
def get_dictionary(self):
return self.list_of_samoloty
def get_loty_by_numer_lotu(self, numer):
print(self.list_of_samoloty[2])
print(numer)
return self.list_of_samoloty[numer]
When I tried to run this I got this as an output:
{1: <lociki.Lot object at 0x7f4443f9ec70>}
{1: <lociki.Lot object at 0x7f4443f9ec70>, 2: <lociki.Lot object at 0x7f4443f9edc0>}
{1: <lociki.Lot object at 0x7f4443fa2c10>}
{1: <lociki.Lot object at 0x7f4443fa2c10>, 2: <lociki.Lot object at 0x7f4443f42670>}
<lociki.Lot object at 0x7f4443f42670>
2
KeyError: '2'
2 (int), which is your key is not the same as '2' (str), which is what numer contains

Setting nested Dictionary in Python: Why class method behaves differently from an standalone function?

Consider this simplified class:
class test(object):
def __init__(self):
self.inner_dict = {}
def nested_set_method(self, keys,value=None):
end = len(keys) - 1
for index, component in enumerate(keys):
if index < end or value is None:
self.inner_dict = self.inner_dict.setdefault(component, {})
else:
self.inner_dict[component] = value
and this function which is identical to nested_set_method of the above class:
def nested_set_standalone(input_dict, keys,value=None):
end = len(keys) - 1
for index, component in enumerate(keys):
if index < end or value is None:
input_dict = input_dict.setdefault(component, {})
else:
input_dict[component] = value
This is a sample usage of the class:
>>> a = test()
>>> a.inner_dict
{}
>>> a.nested_set_method([1,2,3,4],'l')
>>> a.inner_dict
{4: 'l'}
This is the sample usage of the function over an instance of the class:
>>> b = test()
>>> b.inner_dict
{}
>>> nested_set_standalone(b.inner_dict,[1,2,3,4],'l')
>>> b.inner_dict
{1: {2: {3: {4: 'l'}}}}
I expected the class's nested_set_method with this output {4: 'l'} to have the same output as the function nested_set_standalone which is {1: {2: {3: {4: 'l'}}}}.
But why are they different?
EDIT: I ran these examples on Python 3.6.6
inner_dict in the function is a local variable, but in the method it changes the attribute. Simply, use a local variable, too:
class test(object):
def __init__(self):
self.inner_dict = {}
def get_nested_dict(self, keys):
inner_dict = self.inner_dict
for component in keys:
inner_dict = inner_dict.setdefault(component, {})
return inner_dict
def nested_set_method(self, keys,value=None):
if value is None:
return self.get_nested_dict(keys)
else:
inner_dict = self.get_nested_dict(keys[:-1])
inner_dict[keys[-1]] = value

Python, update initialized dictionary that works as class attribute

I add dynamically new values to a dictionary. When I call it, I am expecting it to be loaded with the recently added values.
class Elements():
def __init__(self, length):
self.dict = {}
self.length = length
self.init_dict()
def init_dict(self):
self.dict[0] = self.length
return self.dict[0]
def dict_update(self):
self.dict.update({1: self.dict[0] + 1})
return self.dict
Elements(100)
print Elements(100).dict
print Elements(100).dict_update()
print Elements(100).dict
This returns back:
{0: 100}, {0: 100, 1: 101}, {0: 100}
whereas I am expecting
{0: 100}, {0: 100, 1: 101}, {0: 100, 1: 101}
Let me explain:
Elements(100) # New element created.
print Elements(100).dict # Print dict from a new element created.
print Elements(100).dict_update() # Print what is returned from dict_update from a new element created. In this case, the dict is updated as well.
print Elements(100).dict # Print dict from a new element created. So this object is not related to the old updated one.
So you are printing the dict value from a newly created object of Element, and it is not related to the object you updated.
To fix this problem, you need to refer to 1 object only.
ele = Elements(100)
print ele.dict
print ele.dict_update()
print ele.dict
Try this:
class Elements():
def __init__(self, length):
self.dict = {}
self.length = length
self.init_dict()
def init_dict(self):
self.dict[0] = self.length
return self.dict[0]
def dict_update(self):
self.dict.update({1: self.dict[0] + 1})
return self.dict
E = Elements(100)
print E.dict
print E.dict_update()
print E.dict

Obtaining values from dictionary in OOP: "AttributeError"

I am trying to get values from my dictionary VALUES. My program creates combination of possible positions and gets the last position. Then I want to get the value. Everything works well here except indicated .get_value method. When I execute this code I receive:
AttributeError: 'Combination' object has no attribute 'get_value'
Theoretically it should be easy but I am new to OOP and I don't see what is wrong here.
X = ['A','B','C']
Y = ['1','2','3']
VALUES = {'A':10, 'B': 50, 'C':-20}
class Combination:
def __init__(self,x,y):
if (x in X) and (y in Y):
self.x = x
self.y = y
else:
print "WRONG!!"
def __repr__ (self):
return self.x+self.y
def get_x(self):
return self.x
def get_y(self):
return self.y
class Position:
def __init__(self):
self.xy = []
for i in X:
for j in Y:
self.xy.append(Combination(i,j))
def choose_last(self):
return self.xy.pop()
def __str__(self):
return "List contains: " + str(self.xy)
class Operation1:
def __init__(self):
self.operation1 = []
def __str__(self):
s = str(self.operation1)
return s
def get_value(self):
V = VALUES.get(self)
return V
pos = Position()
print pos
last_item = pos.choose_last()
print "Last item:", last_item, pos
last_value = last_item.get_value() # <---- Here is a problem
How can I obtain value of my position? Value is determined by the X value - this is A,B or C. In the dictionary I have a numeral value for the letter.
You are appending objects of Combination into xy of Position. When you say choose_last, it will return the last Combination object inserted into xy. And you are trying to invoke get_value method on a Combination object, which doesnt have that method. Thats why you are getting that error.
Always use new style classes.

Autogrowing list in Python?

I need a list-like object that will "autogrow" whenever a slot number greater or equal to its length is accessed, filling up all the newly created slots with some pre-specified default value. E.g.:
# hypothetical DefaultList class
x = DefaultList(list('abc'), default='*')
x[6] = 'g'
print x[2], x[4], x[6], x[8] # should print 'c * g *'
Thanks!
PS. I know it is not hard to implement a class like this, but I avoid wheel-reinvention as much as possible, especially if a particularly efficient/well-designed wheel already exists.
PS2. A dict (or a collections.defaultdict) is not an acceptable implementation of the desired data structure. For why, see here: http://groups.google.com/group/comp.lang.python/msg/bcf360dfe8e868d1?hl=en
class DefaultList(list):
def __init__(self,*args,**kwargs):
list.__init__(self,*args)
self.default=kwargs.get('default',None)
def __getitem__(self,key):
# retrieving an item does not expand the list
if isinstance(key,slice):
return [self[elt] for elt in range(key.start,key.stop,key.step)]
else:
try:
return list.__getitem__(self,key)
except IndexError:
return self.default
def __setitem__(self,key,value):
# setting an item may expand the list
try:
list.__setitem__(self,key,value)
except IndexError:
self.extend([self.default]*(key-len(self)))
self.append(value)
x = DefaultList(list('abc'), default='*')
print(x)
# ['a', 'b', 'c']
x[6] = 'g'
print(x)
# ['a', 'b', 'c', '*', '*', '*', 'g']
print x[2], x[4], x[6], x[8] # should print 'c * g *'
# c * g *
print(x[2:9:2])
# ['c', '*', 'g', '*']
I would use a sparse data structure (1xn matrix).
You could always make a function that handles this:
def fillList(item, slot, myList):
length = len(myList)
if slot > length:
augmentation = [item for x in range(slot-length)]
myList.extend(augmentation)
else:
myList[slot] = item
Which while not a data structure, does accomplish what you want.
Using the idea of wheaties's solution and making a prettier interface:
You could inherit from list and overwrite the list 'getitem(index)' method which maps to [index] in your class. It should be something like this:
class GrowingList(list):
def __getitem__(self, index):
length = len(self)
# list is 0 indexed
if index >= length:
tail = [ self.default_value for x in range(index - length + 1)]
self.extend(tail)
return super(self.__class__, self).__getitem__(index)
This same code can be used if you don't extend the list, but just return some default value on invalid index
This preserves the whole list interface.
(This isn't a new answer; just a comment on unutbu's. It should really be possible to post stuff like this in comments; it isn't, so I have to post it as an answer.)
CombineListClasses and CombineListClasses2 inherit from two classes that both inherit from list. The behavior and doctests are straightforward, but break badly in the original version.
This is all standard practice in Python's data model; you almost never should be calling a base class method directly rather than via super.
class DefaultList(list):
"""
>>> x = DefaultList('abc', default='*')
>>> x
['a', 'b', 'c']
>>> x[6] = 'g'
>>> x
['a', 'b', 'c', '*', '*', '*', 'g']
>>> x[2], x[4], x[6], x[8] # should print 'c * g *'
('c', '*', 'g', '*')
>>> x[2:9:2]
['c', '*', 'g', '*']
>>> x = DefaultList()
>>> x[1] = 'a'
>>> x
[None, 'a']
>>> x = DefaultList(sequence=[1,2,3], default=5)
>>> x
[1, 2, 3]
>>> x[10]
5
"""
def __init__(self, *args, **kwargs):
if 'default' in kwargs:
self.default = kwargs['default']
del kwargs['default']
else:
self.default = None
super(DefaultList, self).__init__(*args, **kwargs)
def __getitem__(self, key):
# retrieving an item does not expand the list
if isinstance(key, slice):
return [self[elt] for elt in range(key.start, key.stop, key.step)]
else:
try:
return super(DefaultList, self).__getitem__(key)
except IndexError:
return self.default
def __setitem__(self, key, value):
# setting an item may expand the list
try:
super(DefaultList, self).__setitem__(key, value)
except IndexError:
self.extend([self.default]*(key-len(self)))
self.append(value)
# Another class that derives from list:
class AddMethodToList(list):
def __init__(self, *args, **kwargs):
self.value = kwargs['value']
del kwargs['value']
super(AddMethodToList, self).__init__(*args, **kwargs)
def new_method(self):
return self.value
# Derive from both classes.
class CombineListClasses(AddMethodToList, DefaultList):
"""
>>> a = CombineListClasses(default=10, sequence=[1,2,3], value=3)
>>> a.new_method()
3
>>> a[5] = 1
>>> a
[1, 2, 3, 10, 10, 1]
"""
pass
# Derive from both classes in reverse, reversing the call chain order.
class CombineListClasses2(DefaultList, AddMethodToList):
"""
>>> a = CombineListClasses2(default=10, sequence=[1,2,3], value=3)
>>> a.new_method()
3
>>> a[5] = 1
>>> a
[1, 2, 3, 10, 10, 1]
"""
pass
if __name__ == '__main__':
import doctest
print doctest.testmod()
Note that in Python 3, this is supported by the language directly:
class DefaultList(list):
def __init__(self, *args, default=None, **kwargs):
self.default = default
super(self).__init__(*args, **kwargs)
but that's not supported in Python 2. http://www.python.org/dev/peps/pep-3102

Categories