How to use the same line of code in all functions? - python

I am newbie in Python.
I wonder if it is possible that all functions inherit the same line of code?
with open(filename, 'r') as f: as this line of code is the same in all three functions. Is it possible to inherit the code without using classes?
I tried to find the answer on stackoverflow and python documentation, but with no luck.
def word_count(filename):
with open(filename, 'r') as f:
return len(f.read().split())
def line_count(filename):
with open(filename, 'r') as f:
return len(f.read().splitlines())
def character_count(filename):
with open(filename, 'r') as f:
return len(f.read())

The common code in your case is
with open(filename, 'r') as f:
contents = f.read()
So just move it to its own function:
def get_file_contents(filename):
with open(filename, 'r') as f:
return f.read()
def word_count(filename):
return len(get_file_contents(filename).split())
def line_count(filename):
return len(get_file_contents(filename).splitlines())
def character_count(filename):
return len(get_file_contents(filename))

What I've done in the past is split the code out into another function, in your example
with open(filename, 'r') as f:
f.read()
Is common within all of your methods, so I'd look at rewriting it like so.
def read_file(filename):
with open(filename, 'r') as f:
return f.read()
def word_count(filename):
return len(read_file(filename).split())
def line_count(filename):
return len(read_file(filename).splitlines())
def character_count(filename):
return len(read_file(filename))

I would use a class:
class Count:
""" Object holds everything count-related """
def __init__(self, filename):
""" specify filename in class instance """
with open(filename, 'r') as f:
self.content = f.read()
def word_count(self):
return len(self.content.split())
def line_count(self):
return len(self.content.splitlines())
def character_count(self):
return len(self.content)
file = Count("whatever.txt")
print(file.word_count())
print(file.line_count())
print(file.character_count())

What you do differently is after you open the file, so if I were in your shoes, I would write a function which takes another function that is executed after the file is opened.
Let's illustrate this in an example:
>>> def operate_file(filename, func):
... with open(filename, 'r') as f:
... return func(f)
>>> def line_count(f):
... return len(f.read().splitlines())
>>> def word_count(f):
... return len(f.read().split())
>>> def character_count(f):
... return len(f.read())
>>> print operate_file('/tmp/file.txt', line_count)
1200
>>> print operate_file('/tmp/file.txt', word_count)
2800
>>> print operate_file('/tmp/file.txt', character_count)
29750

I would recommend decorators. It's sort of like the making the repeated line of code into a function, but since you are going to call that function on each input anyway, decorators can let you just write the functions as id f was the input.
The #open_file is a shorthand for word_count=open_file(word_count).
here is a good place to read more about python decorators.
def open_file(func):
def wrapped_func(filename):
with open(filename, 'r') as f:
return func(f)
return wrapped_func
#open_file
def word_count(f):
return len(f.read().split())
#open_file
def line_count(f):
return len(f.read().splitlines())
#open_file
def character_count(f):
return len(f.read())

It depends on, what you want to do with the results of your 3 functions. Every function is opening the same file. That happens 3 times just to get 3 different properties.
One good solution would be a class. But another would be to rearange your functions to just one. That could return a dictionary or named tuple with the results.
It would look something like this:
def file_count(filename):
with open(filename, 'r') as f:
content = f.read()
properties = {}
properties['words'] = len(content.split())
properties['lines'] = len(content.splitlines())
properties['chars'] = len(content)
return properties

Related

Python: Error in assigning a method result to a instance variable on a constructor

When I run this code I get a message saying that name 'readFile' is not defined. How can I write this so that I don't have this error? I want to assign a list of lists to self.cities. Thank you.
class TSP:
def __init__(self, filename):
self.filename = filename
self.cities = readFile()
def readFile(self):
f = open(self.filename, 'r')
citieslist = []
res = f.readlines()
for line in res:
aList = list(line.split(';'))
for i in range(0,len(aList)):
aList[i] = aList[i].rstrip('\n')
citieslist.append(aList)
return readFile (self.cities)
f.close()
You have not used self in init. You have a recursive function at readFile. You closed the file after returning from function readFile. You only have to strip the whole line to cut the \n off. Also returning is unnecessary since you can work with references inside Class.
class TSP:
def __init__(self, filename):
self.filename = filename
self.cities = self.readFile()
def readFile(self):
f = open(self.filename, 'r')
citieslist = []
res = f.readlines()
for city in res:
city = city.strip().split(';')
citieslist.append(city)
f.close()
return citieslist
SInce you have basically negated any future use of readFile by omitting an argument for filename in it's interface, you could just do the below.
We simply use a with statement to process the file, and a list comprehension to concoct the results.
class TSP:
def __init__(self, filename):
with open(filename, 'r') as f:
self.cities = [line.strip().split(';') for line in f.readlines()]
#do something with self.cities here
tsp = TSP('somefile.ext')

Get a parameter of a file .txt using python

I'm trying to get parameters of a .txt like that:
a=10
b=15
c=20
How can i do a function called get() that takes from this file called parameters.txt the parameter b and return 15??
It's to build a module that works like a getter of parameters.
To simplify and explain information: the .txt conatains parameters with and int value. The get() function will obtain the integer value associated to the parameter.
Since you say you want "to build a module that works like a getter of parameters", you should just parse the whole file into an internal dictionary:
class Config(dict):
def __init__(self, file_name):
with open(file_name) as f:
for line in f:
key, value = line.strip().split("=")
self[key] = value
Example usage:
c = Config("test.txt")
print(c)
# {'a': '10', 'b': '15', 'c': '20'}
print(c['b'])
# 15
If all your values are numerical, you might want to modify this to do self[key] = float(value) or similar. Otherwise you might want to define a try_parse_numeric function:
def try_parse_numeric(s):
try:
return int(s)
except ValueError:
pass
try:
return float(s)
except ValueError:
pass
return s
class Config(dict):
def __init__(self, file_name, value_parser=try_parse_numeric):
self.value_parser = value_parser
with open(file_name) as f:
for line in f:
key, value = line.strip().split("=")
self[key] = self.value_parser(value)
And if it gets more complicated than that, you probably want to use configparser instead of rolling your own.
def get(param):
with open(filename, "r") as infile: #Read file
for line in infile: #Iterate over each line
if line.startswith(param): #Check if line starts with input param
return line.split("=")[1] #Return Value
print(get("b"))
def get(par):
with open("parameters.txt",'r') as file:
return next(e for e in file.readlines() if par in e).split("=")[1]
Try this
Something like this sould work:
def get(filename,variable):
with open(filename) as f:
for line in f.readlines():
if line[:len(variable)] == variable:
return line.split("=")[-1]
return None
print(get("test.txt","b"))

How to "chain" iterators? [duplicate]

This question already has answers here:
How to join two generators (or other iterables) in Python?
(15 answers)
Closed last month.
I'm trying to chain iterators together with one iterator reading from a master file and another iterator taking each line of the master file and processing another file depending on the output of the first.
The working code that I have is as follows
class MasterReader(object):
def __init__(self, filename):
self.f = open(filename, "r")
def __iter__(self):
return self
def __next__(self):
line = self.f.readline().strip()
if line == "":
raise StopIteration
return line
class SubReader(object):
def __init__(self, mr):
self.mr = mr
def __iter__(self):
self._next()
return self
def _next(self):
self.current = open(self.mr.__next__(), "r")
def __next__(self):
while True:
line = self.current.readline().strip()
if line == "":
self._next()
continue
return line
mr = MasterReader("master")
sr = SubReader(mr)
for line in sr:
print(line)
Where master is a file containing lines of other files
file1
file2
file1 contains
1.a
1.b
1.c
file2 contains
2.a
2.b
2.c
The output is
1.a
1.b
1.c
2.a
2.b
2.c
Again what I have works, but feels wrong in that I have a while loop in __next__ I'm having to manually check for the end of each sub file and explicitly calling the next line in the master file.
Is there a better/more pythonic way of doing this?
EDIT:
This is a simplified problem of what I'm trying to accomplish. In the real version SubReader is going to be threaded and I only want one MasterReader. Actually this won't work for my threading project but want to make sure I'm generalizing iterators before diving deeper into a mess.
You could use itertools.chain.from_iterable with the help of small function yielding the stripped lines from each file.
from itertools import chain
def fgen(fname):
with open(fname) as f:
for line in f:
yield line.strip()
for a in chain.from_iterable(fgen(line) for line in fgen('master.txt')):
print(a)
Since the file object is itself an iterator, you don't necessarily need to implement a __next__ in both cases, just yield lines from it in your __iter__. More so, reading the file with a for loop implicitly handles EOF:
class MasterReader(object):
def __init__(self, filename):
self.f = open(filename)
def __iter__(self):
for line in self.f:
yield line.strip()
self.f.close()
class SubReader(object):
def __init__(self, mr):
self.mr = mr
def __iter__(self):
for filename in mr:
with open(filename) as f:
for line in f:
yield line.strip()

Scope of nested function declarations

I have the following code:
def function_reader(path):
line_no = 0
with open(path, "r") as myfile:
def readline():
line_no +=1
return myfile.readline()
Python keeps returning:
UnboundLocalError: local variable 'line_no' referenced before assignment
when executing line_no +=1.
I understand that the problem is that nested function declarations have weird scoping in python (though I do not understand why it was programmed this way). I'm mostly wondering if there is a simple way to help python resolve the reference, since I really like the functionality this would provide.
Unfortunately, there is not a way to do this in Python 2.x. Nested functions can only read names in the enclosing function, not reassign them.
One workaround would be to make line_no a list and then alter its single item:
def function_reader(path):
line_no = [0]
with open(path, "r") as myfile:
def readline():
line_no[0] += 1
return myfile.readline()
You would then access the line number via line_no[0]. Below is a demonstration:
>>> def outer():
... data = [0]
... def inner():
... data[0] += 1
... inner()
... return data[0]
...
>>> outer()
1
>>>
This solution works because we are not reassigning the name line_no, only mutating the object that it references.
Note that in Python 3.x, this problem would be easily solved using the nonlocal statement:
def function_reader(path):
line_no = 0
with open(path, "r") as myfile:
def readline():
nonlocal line_no
line_no += 1
return myfile.readline()
It's hard to say what you're trying to achieve here by using closures. But the problem is that with this approach either you'll end with an ValueError: I/O operation on closed file when you return readline from the outer function or just the first line if you return readline() from the outer function.
If all you wanted to do is call readline() repeatedly or loop over the file and also remember the current line number then better use a class:
class FileReader(object):
def __init__(self, path):
self.line_no = 0
self.file = open(path)
def __enter__(self):
return self
def __iter__(self):
return self
def next(self):
line = next(self.file)
self.line_no += 1
return line
def readline(self):
return next(self)
def __exit__(self, *args):
self.file.close()
Usage:
with FileReader('file.txt') as f:
print next(f)
print next(f)
print f.readline()
print f.line_no # prints 3
for _ in xrange(3):
print f.readline()
print f.line_no # prints 6
for line in f:
print line
break
print f.line_no # prints 7
The more Pythonic way to get the next line and keep track of the line number is with the enumerate builtin:
with open(path, "r") as my file:
for no, line in enumerate(myfile, start=1):
# process line
This will work in all current Python versions.

Use the _init_ definition in order to initialize an object in Python

I have written an algorithm in Python and now I am trying to make it a bit more object oriented. I have a good understanding (I think) of objects and classes and I have spend some time reading online the syntax for classes in Python. However, I guess my question is quite basic and it would be great to get some help.
I have created a Class XML which contains 3 definitions. I also have used __init__ to initialize the object.
class XML():
def __init__(self,f):
self.f = f
def xmlToString(self):
data = self.f.read()
self.f.close()
...
return station_arr
def exportArray(self):
f= open('stations/'+self.STATION+'.txt')
lines= f.readlines()
...
return phenomena,parameters
def calcAvg(self):
split_phenom = self.phenomena.split(';')
list_of_lists = []
for e in self.parameters:
...
return phenomena,parameters
Then, in the main.py I instantiate the objects and call the methods I want like this:
stations_names ['one', 'two'...]
for station in stations_names:
f = open('respond.txt','r')
xmlStr = ClassXML.XML(f)
stations_arr = xmlStr.xmlToString()
xmlRead = ClassXML.XML(stations_arr)
phenomena,parameters = xmlRead.exportArray()
xmlRetr = ClassXML.XML(phenomena,parameters)
avg_dict,dict_values = xmlRetr.calcAvg()
The error I get is this:
f= open('stations/'+self.station+'.txt')
AttributeError: XML instance has no attribute 'station'
So I understand what is the problem. Some how I have to pass into the class the variable "station". But when I try to included it in the init function I get different errors:
xmlStr = ClassXML.XML(f)
TypeError: __init__() takes exactly 3 arguments (2 given)
Then I thought maybe I have to have multiple init functions but as far as I know this is not possible in Python.
To be honest I don't really know how to handle the problem. Any tip would be useful.
Thanks
D
P.s. I am not sure if the title explains correctly my question, but I can not find any correct words to put it!
IMPLEMENTED FINAL ANSWER
class XML():
def __init__(self,f,station):
self.f = f
self.station =station
def xmlToString(self):
data = self.f.read()
self.f.close()
...
return station_arr
def exportArray(self):
f= open('stations/'+self.STATION+'.txt')
lines= f.readlines()
...
return phenomena,parameters
def calcAvg(self,phenomena,parameters):
split_phenom = self.phenomena.split(';')
list_of_lists = []
for e in self.parameters:
...
return avg_dict,dict_values
** Main **:
for station in stations_names:
f = open('respond.txt','r')
## Instantiate class: ClassXmlString
xmlStr = ClassXML.XML(f,station)
stations_arr = xmlStr.xmlToString()
if stations_arr !='':
phenomena,parameters = xmlStr.exportArray()
avg_dict,dict_values = xmlStr.calcAvg(phenomena,parameters)
class XML():
def __init__(self,f,station):
self.f = f
self.station=station
def xmlToString(self):
data = self.f.read()
self.f.close()
...
self.station_arr = station_arr
def exportArray(self):
#here you need to use self.station_arr
f= open('stations/'+self.station+'.txt')
lines= f.readlines()
...
self.phenomena=phenomena
self.parameters=parameters
def calcAvg(self,):
#here you need to use self.phenomena and self.parameters
split_phenom = self.phenomena.split(';')
list_of_lists = []
for e in self.parameters:
...
self.avg_dict = avg_dict
self.dict_values = dict_values
def makeOutput(self):
#call all your functions
self.xmlToString()
self.exportArray()
self.scalcAvg()
return self.avg_dict , self.dict_values
#now in your main you need to instanciate your class once! not each time you need to call a method:
stations_names ['one', 'two'...]
for station in stations_names:
f = open('respond.txt','r')
xmlStr = ClassXML.XML(f,station)
avg_dict,dict_values = xmlStr.makeOutput()
Haven't tried it, but should work.
I think you could change the way the solution is organized, to make things a bit easier.
Based on the code you posted, I'm assuming that:
xmlToString takes the file f and station as parameter
exportArray takes stations_arr as parameter
calcAvg takes (phenomena, parameters) as parameter
I'll also assume you are ultimately interested in the (avg_dict, dict_values). That said, a slightly refactored version of this solution be something like this:
Main code:
stations_names ['one', 'two'...]
for station in stations_names:
my_xml_object = ClassXML.XML('respond.txt', station)
avg_dict, dict_values = my_xml_object.calcAvg()
Class:
class XML():
def __init__(self, f_name, station):
# 1 - define self.data
with open(f_name, 'r') as f:
self.data = f.read()
# 2 - define self.station_arr
self.station_arr = self.xmlToString(station)
# 3 - Finally define (phenomena, parameters), which
# will be used by calcAvg()
self.phenomena, self.parameters = self.exportArray(station_arr)
def xmlToString(self, station):
data = self.data
...
return station_arr
def exportArray(self, station_arr):
# you must define self.STATION somewhere
f = open('stations/' + self.STATION + '.txt')
lines = f.readlines()
...
return phenomena, parameters
def calcAvg(self):
split_phenom = self.phenomena.split(';')
list_of_lists = []
for e in self.parameters:
...
return phenomena, parameters
I havent't test it, but the most important is that you get the idea.

Categories

Resources