I have some questions about "technical" and basic functions in python.
I have a table like this:
Name,Gender,Age,Salary,Height
Menny, M, 1, 1, 1
James, J, 2, 2, 2
Sami, S, 3, 3, 3
class Table:
def __init__(self,path,sep):
try:
f = open(path, "r")
read_file = f.read()
f.close()
except:
print "cannot create a table from this file"
return
table = read_file.split("\n")
for i in range (len(table)):
table[i] = table[i].split(sep)
if len(table) > 0:
for i in range(len(table[0])):
if table[0][i] in table[0][0:i]:
raise ValueError
row_names = []
for i in range(1,len(table)):
if len(table[i]) != len(table[0]):
raise ValueError
if table[i][0] in row_names:
raise ValueError
row_names.append(table[i][0])
Now I want to use functions:
1. to know how many cells there are. here I have 12 cells. The height of the table is len(table). Then the width is len(table[0]). The number of cells is height*width.
so:
def len(self):
height = len(table)
width = len(table[0])
return height * width
and if I tried this:
def len(self):
len(self.nestedList)*len(self.nestedList[0])
I get "None"
If in the shell I write the name Menny, Sami etc, then print the rest of the line (age, salary etc)....
So I thought about it:
def the_row (self, rowname):
rows_checking = []
for i in range(1, len(table)):
rows_checking.append(table[i])
if rowname in rows_checking:
table[i].remove(table[0:0])
return table[i]
almost the same thing like in the second task, but this time the function will print the value that is common to 2 thing. For example - the code will print "1" if I write Menny and Age.
Again, I think I'll do it almost the same as I did in the pre task, but this time:
get_the_value(self,rowname,colname)
So far seems to be good ideas, I hope so...
but I get errors:
AttributeError: Table instance has no attribute '__len__'
or
AttributeError: Table instance has no attribute 'len'
Probably because I didn't used "self" here, right? So what I can do?
You don't have to feed me by the spoon and tell me the codes as it should be, but just give me advices as possible as you can, please.
edited code:
class Table:
def __init__(self,path,sep):
self.path=path
self.sep=sep
self.g=[]
self.count=0
self.headlines=[]
self.matrix=[]
self.headrows=[]
self.postionrow=0
self.postioncolmn=0
try:
f=open(self.path,'r')
read_file=f.read()
split_file=read_file.split()
for line in split_file:
list_the_line=line.split(self.sep)
self.g.append(list_the_line)
self.count=0
for z in range (len(self.g[0])):
self.count=0
for d in range(len(self.g[0])):
if self.g[0][z]==self.g[0][d]:
self.count+=1
if self.count>=2:
raise ValueError
num_first_line=len(self.g[0])
for k in range (len(self.g)):
if len(self.g[k])!= num_first_line:
raise ValueError
self.headlines=self.g[0]
self.g.remove(self.g[0])
self.count=0
for row_name1 in range (len(self.g)):
self.count=0
for row_name2 in range(len(self.g)):
if self.g[row_name1][0]==self.g[row_name2][0]:
self.count+=1
if self.count>=2:
raise ValueError
for i in range (len(self.g)):
self.headrows.append(self.g[i][0])
self.g[i].remove(self.g[i][0])
ezer=[]
for op in range (len(self.g)):
ezer=[]
for od in range (len(self.g[0])):
ezer.append(self.g[od][op])
self.matrix.append(ezer)
f.close()
except :
print "cannot creat a table object from this file"
return
def len(self):
num_rows=len(self.g)
num_cols=len(self.g[0])
return num_rows*num_cols
def get_row(self,rowname):
for i in range (len(self.headlines)):
if rowname==self.headrows[i]:
self.postionrow=i
return self.g[i]
if not rowname in self.headrows :
raise ValueError
def get_column(self,colname):
for i in range (len(self.headlines)):
if colname==self.headlines[i]:
self.postioncolmn=i-1
return self.matrix[i-1]
if not colname in self.headlines :
raise ValueError
def get_value(self,rowname,colname):
self.get_row(rowname)
self.get_column(colname)
if not rowname in self.headrows :
raise ValueError
if not colname in self.headlines :
raise ValueError
return self.g[self.postionrow][self.postioncolmn]
def get_row_name_with_max_value(self,colname):
if not colname in self.headlines :
raise ValueError
max_colmn=max(self.get_column(colname))
for i in range (len(self.matrix)):
if max_colmn == self.g[i][self.postioncolmn]:
return self.headrows[i]
and what should be the result:
>>> table = Table("table_examp1111111","\t")
cannot create a table from this file
>>> table = Table("table_example1.txt","\t")
>>> print table.len()
12
>>> print table.get_row("Menny")
['M', '1', '1', '1']
>>> print table.get_column("Height")
['1', '2', '3']
>>> print table.get_value("Sami","Age")
3
>>> print table.get_row_name_with_max_value("Height")
Sami
>>> print table.get_row_name_with_max_value("Salary")
Sami
This code works but I want to make it more pythonic. Please don't change the form, don't add or remove function just fix my syntex.
Thanks.
Whenever you call the function len() on an object. It will try to call the __ len__ function of that object. So if you do that it might work.
def __len__(self):
height = len(self.table)
width = len(self.table[0])
return height * width
you are tying to call __len__ on the Table class, while you look like you should be calling it on your table string array in the constructor.
You should create an attribute self.table, and then either use the len function on that, or
def numOfCells(self):
return len(self.table) * len(self.table[0])
This looks like a perfect place to use the csv module:
import csv
def load_csv(fname, **kwargs):
with open(fname, 'rb') as inf:
in_csv = csv.reader(inf, **kwargs)
return list(in_csv)
class Table:
def __init__(self, path, sep=','):
self.table = load_csv(path, delimiter=sep)
if len(self.table) == 0:
raise ValueError('No data in file {}'.format(path))
self.header = self.table.pop(0)
self.cols = len(self.header)
self.labels = {}
for i, row in enumerate(self.table, 1):
# make sure rows are all same length
if len(row) != self.cols:
raise ValueError('row {} contains {} items - should be {}'.format(i, len(row), self.cols))
# make sure rows-labels are unique
lbl = row[0]
if lbl in self.labels:
raise ValueError('rows {} and {} - duplicate labels'.format(self.labels[lbl], i))
else:
self.labels[lbl] = i - 1
#property
def rows(self):
return len(self.table)
#property
def cells(self):
return self.rows * (self.cols - 1) # omit row labels
def get_row_by_label(self, lbl):
return self.table[self.labels[lbl]]
def get_item(self, lbl, attr):
ndx = self.header.index(attr)
return self.get_row_by_label(lbl)[ndx]
def main():
t = Table('tbl.csv')
print(t.cells)
print(t.get_row_by_label("Menny"))
print(t.get_item("Menny", "Age"))
if __name__=="__main__":
main()
EDIT:
Ok, this is for your FIRST question. From what I understand, you are wanting a function that will return the number of cells in your table. This number does not include the names of people in the rows, and does not include the first row at all. If I understand correctly, then this should work:
If table is:
Name,Gender,Age,Salary,Height
Menny, M, 1, 1, 1
James, J, 2, 2, 2
Sami, S, 3, 3, 3
Then number of cells is '12'... so:
Example:
class Table:
def __init__(self, path, sep):
try:
with open(path) as f:
read_file = f.read() # will auto close the file after the read
except:
print "cannot create a table from this file"
return
self.table = read_file.split('\n') # self.table will make this easier
self.table = [self.table[i].split(sep) for i in range(len(self.table))] # does the same as your for loop
if len(self.table) > 0:
for i in range(len(self.table[0])):
if self.table[0][i] in self.table[0][0:i]:
raise ValueError
row_names = []
for i in range(1,len(self.table)):
if len(self.table[i]) != len(self.table[0]):
raise ValueError
if self.table[i][0] in row_names:
raise ValueError
row_names.append(self.table[i][0])
# now a function that could return the table length
def get_num_cells(self):
# essentially you sum each item in row[1:] for table[1:]
return sum((sum(1 for i in range(1, len(self.table[0])))) for i in range(1,len(self.table)))
Using self.table will make this easier, as you don't have to include it in the other function args, as above in get_num_cells, I just used self.table without putting it in the args of the function.
To call this function you would do the following:
app = Table(path, sep)
app.get_num_cells()
# this code would be placed at the end of your file, and not inside the class
Example:
class Table()
__init__(self, path, sep):
etc.
etc.etc.etc.
# now the code to create an instance of Table and call a method here like this
app = Table(path, sep) # path would be a filepath "C:/etc./file.txt", and sep = "," etc.
app.get_num_cells()
For your other questions, I am not entirely sure what you want yet, but if you write again in the comments for this, I will try. Please let me know if this works for you.
Related
I have a python problem that creates a box and puts items in the box and with the help of the list command I can see what's in my box. One example of the execution may go as:
next command> newbox bedroom-box-05
next command> add bedroom-box-05 pillow 3
next command> list bedroom-box-05
Box "bedroom-box-05" contains 3 items.
3 pillow
There are some issues with class MovingBox() as I cannot change the main functions.
class MovingBox():
"""A class for keeping track of the contents ofa moving box. """
def __init__(self,*args):
self = dict()
def add_item(self,key,value =[]):
setattr(self,key,value)
self.add_item=value
def list_content(self,key,value =[]):
setattr(self, key, value)
self.list_content = value
key1 = Keys() # parens
key1.list_content(value)
DON'T CHANGE ANYTHING AFTER THIS LINE
def convert_str_to_int(word):
"""
Converts the parameter string *word* in to an integer value.
"""
try:
result = int(word)
except ValueError:
return None
return result
def newbox(all_boxes, list_of_additional_info):
if len(list_of_additional_info) != 1:
print("Error: wrong number of initial data: can't create a new box.")
return
box_name = list_of_additional_info[0]
all_boxes[box_name] = MovingBox(box_name)
def add_to_box(all_boxes, list_of_additional_info):
if len(list_of_additional_info) != 3:
print("Error: wrong number of elements: can't add into a box.")
return
box_name, item_name, item_count = list_of_additional_info
item_count = convert_str_to_int(item_count)
if item_count is None:
print("Error: not a number: can't add to a box.")
return
if box_name not in all_boxes:
print("Error: box does not exist: can't add to a box.")
return
all_boxes[box_name].add_item(item_name, item_count)
def list_box_content(all_boxes, list_of_additional_info):
"""Displays the contents of a single box in *all_boxes* """
if len(list_of_additional_info) != 1:
print("Error: wrong number of elements: can't list contents.")
return
box_name = list_of_additional_info[0]
if box_name not in all_boxes:
print("Error: box does not exist: can't list content.")
return
all_boxes[box_name].list_content()
def main():
boxes = {}
while True:
command_line = input("next command> ").strip()
if command_line == "":
break
command_words = command_line.split()
first_word = command_words[0]
list_of_other_words = command_words[1:]
if first_word == "quit":
break
elif first_word == "newbox":
newbox(boxes, list_of_other_words)
elif first_word == "add":
add_to_box(boxes, list_of_other_words)
elif first_word == "list":
list_box_content(boxes, list_of_other_words)
if __name__ == "__main__":
main()
Based on the way this class is being used in the code that you're not supposed to change, I don't think you've understood what the MovingBox is supposed to contain/model. Here are examples of how MovingBox is being used:
MovingBox(box_name)
# creates a box named box_name
all_boxes[box_name].add_item(item_name, item_count)
# increments the count of item_name by item_count
all_boxes[box_name].list_content()
# lists the content of the box
The immediate error you're hitting is that list_content isn't supposed to take a parameter, but the larger problem is that what you've tried to implement doesn't match up at all with what the rest of the code is trying to do -- e.g. your implementation of add_item seems to be trying to add a unique item with an arbitrary value (that defaults to [], which has its own problems), when the calling code is actually providing a count of a type of item.
Since MovingBox is seemingly supposed to just be a counter with a custom interface, I'd implement it as a wrapper around collections.Counter:
from collections import Counter
class MovingBox():
"""A class for keeping track of the contents of a moving box. """
def __init__(self, name: str):
self.name = name
self.contents = Counter()
def add_item(self, name: str, count: int) -> None:
self.contents[name] += count
def list_content(self) -> None:
total = sum(self.contents.values())
print(f'Box "{self.name}" contains {total} items.')
for name, count in self.contents.items():
print(f'{count} {name}')
>>> box = MovingBox("a box")
>>> box.add_item("pillow", 3)
>>> box.list_content()
Box "a box" contains 3 items.
3 pillow
I try to get index of instance of object in a list. And I don't know how to do it without for-loop.
If someone can show me right direction, without for-looping it.
I find that list has instance of it with any()-function but can't get index out of it.
I try to clarify my problem. If any()-fucntion can find that list(self.data) has instance of object. any()-function returns only true/false. Is there function or way to get index of that instance so i can call it.
And code:
class MyClass:
def __init__(self, f):
self.data = []
self.f = open(f, "rb")
self.mod = sys.modules[__name__]
def readFile(self):
f = self.f
try:
self.head = f.read(8)
while True:
length = f.read(4)
if length == b'':
break
c = getattr(self.mod, f.read(4).decode())
if any(isinstance(x, c) for x in self.data):
index = self.data.index(c) #Problem is here
self.data[index].append(f.read(int(length.hex(), 16)))
else:
obj = c(data=f.read(int(length.hex(), 16)))
self.data.append(obj)
f.read(4) #TODO check CRC
finally:
f.close()
enumerate is the way to go here.
...
c = getattr(self.mod, f.read(4).decode())
found = [ (i, x) for (i,x) in enumerate(self.data) if isinstance(x, c) ]
if len(found) > 0:
index, val = found[0]
...
Focusing on getting the instance of the object and its index in the self.data list:
# ...
# This gives you back a class name I assume?
c = getattr(self.mod, f.read(4).decode())
# The following would give you True/False ... which we don't need
# any(isinstance(x, c) for x in self.data)
# Now, this will return _all_ the instances of `c` in data
instances = [x for x in self.data if isinstance(x, c)]
if len(instances):
# Assuming that only 1 instance is there
index = self.data.index(instances[0])
# ??? What do you append here?
self.data[index].append()
else:
obj = c(data=f.read(int(length.hex(), 16)))
self.data.append(obj)
f.read(4) #TODO check CRC
I got a error message in Python:
AttributeError: 'str' object has no attribute 'compute_mark_list'
in my code.
This code suppose to read though a XML file and get the content inside its children:
import libxml2
import sys
'''
purpose
store the information from an answer element
'''
class Answer:
def __init__(self, index, path, result, answer, time):
self.index = index
self.path = path
self.result = result
self.answer = answer
self.time = time
'''
purpose
Store the information from a display element.
'''
class Display:
def __init__(self, index, path, time):
self.index = index
self.path = path
self.time = time
'''
purpose
Extract the information from log_file and return it as a list
of answer and display objects.
preconditions
log_file is the name of a legal, readable quiz log XML file
'''
def load_quiz_log(log_file):
# parse the XML file in argv[2] and load it into memory
parse_tree = libxml2.parseFile(sys.argv[1])
# create a context for tree traversal
context = parse_tree.xpathNewContext()
root = parse_tree.getRootElement()
log_file = root.children
#create a list to hold the objects
L = [ ]
while log_file is not None:
if log_file.name == 'answer':
answer = log_file.content
if log_file.name == 'display':
display = log_file.content
log_file = log_file.next
L = [answer, display]
return L
'''
purpose
Return the number of distinct questions in log_list.
preconditions
log_list was returned by load_quiz_log
'''
def compute_question_count(log_list):
while log_list is not None:
if log_list.name == 'answer':
count += 1
else:
break
log_list = log_list.next
return count
'''
purpose
Extract the list of marks.
For each index value, use the result from the last non-empty answer,
or 0 if there are no non-empty results.
preconditions
log_list was returned by load_quiz_log
'''
def compute_mark_list(log_list):
marks = [ ]
while log_list is not None:
if log_list.name == 'answer':
log_list_answer = log_list.children
while log_list_answer is not None:
if log_list_answer.name == 'index':
index = 0
if log_list_answer.content == index:
result = log_list_answer.next.next.content
index = log_list_answer.content
marks.append(result)
else:
continue
log_list_answer = log_list_answer.next
log_list = log_list.next
return marks
When I run this code I get the error message in the title
import libxml2
import sys
import quiz_library
print '-------------------- test load_quiz_log'
log_list = quiz_library.load_quiz_log(sys.argv[1])
for x in log_list:
if isinstance(x, quiz_library.Answer):
print 'Answer:', x.index, x.path, x.result, x.answer, x.time
else:
print 'Display:', x.index, x.path, x.time
print '-------------------- test compute_question_count'
question_count = quiz_library.compute_question_count(log_list)
print question_count
print '-------------------- test compute_mark_list'
mark_list = quiz_library.compute_mark_list(log_list)
print mark_list
The XML file looks like:
Bear with me here; I am a sysadmin not a developer. The following code works just fine for me. But when I break it into two files so that the class is in one file and the logic is in another I get an error that data[0] is a str and does not support assignment
Striped down working example
class partition:
def __init__(self):
self.data = bytearray(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
return
def boot_flag(self, value=None):
if value is not None:
self.data[0] = value
return
else:
return self.data[0:1][::-1]
part1 = partition()
print str(part1.data).encode('hex')
value = b"\xff"
part1.boot_flag(value)
print str(part1.data).encode('hex')
This is the full class as it stands right now.
class partition:
def __init__(self):
self.data = bytearray(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
def boot_flag(self, value=None):
if value is not None:
self.data[0] = value
return
else:
return self.data[0:1][::-1]
def start_chs(self, value=None):
if value is not None:
self.data[1:4] = value
return
else:
return self.data[1:4][::-1]
def type(self, value=None):
if value is not None:
self.data[4:5] = value
return
else:
return self.data[4:5][::-1]
def end_chs(self, value=None):
if value is not None:
self.data[5:8] = value
else:
return self.data[5:8][::-1]
def start_lba(self, value=None):
if value is not None:
self.data[8:12] = value
else:
return self.data[8:12][::-1]
def sectors(self, value=None):
if value is not None:
self.data[12:16] = value
else:
return self.data[12:16][::-1]
def load(self, data):
self.data = data
return
This is the test jig I am using to test the class.
import dospart
disk = open('/dev/sda', 'rb')
mbr = disk.read(512)
part1 = dospart.partition()
part1.load(mbr[446:462])
print str(part1.data).encode('hex')
part1.boot_flag(b"\xff")
print str(part1.data).encode('hex')
This is the error
Traceback (most recent call last):
File "test.py", line 13, in <module>
part1.boot_flag(b"\xff")
File "/Users/digitaladdictions/PycharmProjects/dospart/dospart.py", line 9, in boot_flag
self.data[0] = value
TypeError: 'str' object does not support item assignment
Note that I can read the values just fine. I only get an error when I try to write to self.data[]
[UPDATE]
Based on the accepted answer this is my new load function which works.
def load(self, data):
part = bytearray(data)
self.data = part
return
I think this is what is happening. When you invoke:
part1.load(mbr[446:462])
self.data is being assigned a string. And that point on, self.data is a string and not a byte array. So when you do
part1.boot_flag(b"\xff")
it rightly complains TypeError: 'str' object does not support item assignment
This is what I mean:
>>> data_one = "My name is shaktimaan"
>>> data_two = data_one[5:10]
>>> type(data_one)
<type 'str'>
In your first case, there is no invocation of load and hence self.data is still a byte array (after calling the constructor). So your boot_flag works as expected without complaining.
I think you need to fix the code in load to assign byte array to self.data
You can't change Python strings inplace, the're immutable. You can find a lot comments about that error "'str' object does not support item assignmen". I don't know how it can work if you combine it in one file.
I want to create a field for phone number input that has 2 text fields (size 3, 3, and 4 respectively) with the common "(" ")" "-" delimiters. Below is my code for the field and the widget, I'm getting the following error when trying to iterate the fields in my form during initial rendering (it happens when the for loop gets to my phone number field):
Caught an exception while rendering: 'NoneType' object is unsubscriptable
class PhoneNumberWidget(forms.MultiWidget):
def __init__(self,attrs=None):
wigs = (forms.TextInput(attrs={'size':'3','maxlength':'3'}),\
forms.TextInput(attrs={'size':'3','maxlength':'3'}),\
forms.TextInput(attrs={'size':'4','maxlength':'4'}))
super(PhoneNumberWidget, self).__init__(wigs, attrs)
def decompress(self, value):
return value or None
def format_output(self, rendered_widgets):
return '('+rendered_widgets[0]+')'+rendered_widgets[1]+'-'+rendered_widgets[2]
class PhoneNumberField(forms.MultiValueField):
widget = PhoneNumberWidget
def __init__(self, *args, **kwargs):
fields=(forms.CharField(max_length=3), forms.CharField(max_length=3), forms.CharField(max_length=4))
super(PhoneNumberField, self).__init__(fields, *args, **kwargs)
def compress(self, data_list):
if data_list[0] in fields.EMPTY_VALUES or data_list[1] in fields.EMPTY_VALUES or data_list[2] in fields.EMPTY_VALUES:
raise fields.ValidateError(u'Enter valid phone number')
return data_list[0]+data_list[1]+data_list[2]
class AdvertiserSumbissionForm(ModelForm):
business_phone_number = PhoneNumberField(required=True)
This uses widget.value_from_datadict() to format the data so no need to subclass a field, just use the existing USPhoneNumberField. Data is stored in db like XXX-XXX-XXXX.
from django import forms
class USPhoneNumberMultiWidget(forms.MultiWidget):
"""
A Widget that splits US Phone number input into three <input type='text'> boxes.
"""
def __init__(self,attrs=None):
widgets = (
forms.TextInput(attrs={'size':'3','maxlength':'3', 'class':'phone'}),
forms.TextInput(attrs={'size':'3','maxlength':'3', 'class':'phone'}),
forms.TextInput(attrs={'size':'4','maxlength':'4', 'class':'phone'}),
)
super(USPhoneNumberMultiWidget, self).__init__(widgets, attrs)
def decompress(self, value):
if value:
return value.split('-')
return (None,None,None)
def value_from_datadict(self, data, files, name):
value = [u'',u'',u'']
# look for keys like name_1, get the index from the end
# and make a new list for the string replacement values
for d in filter(lambda x: x.startswith(name), data):
index = int(d[len(name)+1:])
value[index] = data[d]
if value[0] == value[1] == value[2] == u'':
return None
return u'%s-%s-%s' % tuple(value)
use in a form like so:
from django.contrib.localflavor.us.forms import USPhoneNumberField
class MyForm(forms.Form):
phone = USPhoneNumberField(label="Phone", widget=USPhoneNumberMultiWidget())
I think the value_from_datadict() code can be simplified to:
class USPhoneNumberMultiWidget(forms.MultiWidget):
"""
A Widget that splits US Phone number input into three boxes.
"""
def __init__(self,attrs=None):
widgets = (
forms.TextInput(attrs={'size':'3','maxlength':'3', 'class':'phone'}),
forms.TextInput(attrs={'size':'3','maxlength':'3', 'class':'phone'}),
forms.TextInput(attrs={'size':'4','maxlength':'4', 'class':'phone'}),
)
super(USPhoneNumberMultiWidget, self).__init__(widgets, attrs)
def decompress(self, value):
if value:
return value.split('-')
return [None,None,None]
def value_from_datadict(self, data, files, name):
values = super(USPhoneNumberMultiWidget, self).value_from_datadict(data, files, name)
return u'%s-%s-%s' % values
The value_from_datadict() method for MultiValueWidget already does the following:
def value_from_datadict(self, data, files, name):
return [widget.value_from_datadict(data, files, name + '_%s' % i) for i, widget in enumerate(self.widgets)]
I took hughdbrown's advise and modified USPhoneNumberField to do what I need. The reason I didn't use it initially was that it stores phone numbers as XXX-XXX-XXXX in the DB, I store them as XXXXXXXXXX. So I over-rode the clean method:
class PhoneNumberField(USPhoneNumberField):
def clean(self, value):
super(USPhoneNumberField, self).clean(value)
if value in EMPTY_VALUES:
return u''
value = re.sub('(\(|\)|\s+)', '', smart_unicode(value))
m = phone_digits_re.search(value)
if m:
return u'%s%s%s' % (m.group(1), m.group(2), m.group(3))
raise ValidationError(self.error_messages['invalid'])
Sometimes it is useful to fix the original problem rather than redoing everything. The error you got, "Caught an exception while rendering: 'NoneType' object is unsubscriptable" has a clue. There is a value returned as None(unsubscriptable) when a subscriptable value is expected. The decompress function in PhoneNumberWidget class is a likely culprit. I would suggest returning [] instead of None.