I have a function which returns a list of objects (I used the code below for example). Each object has attribute called text:
def mylist():
mylist = []
for i in range(5):
elem = myobject(i)
mylist.append(elem)
return mylist
for obj in mylist():
print obj.text
How can I rewrite this code so mylist() returned each iteration new value and I iterate over iterator? In other words how can I reuse here a mylist in python so use it like xrange()?
If I understood right, you're looking for generators:
def mylist():
for i in range(5):
elem = myobject(i)
yield elem
Complete code for you to play with:
class myobject:
def __init__(self, i):
self.text = 'hello ' + str(i)
def mylist():
for i in range(5):
elem = myobject(i)
yield elem
for obj in mylist():
print obj.text
You can also use a generator expression:
mylist = (myobject(i) for i in range(5))
This will give you an actual generator but without having to declare a function beforehand.
Please note the usage of parentheses instead of brackets to denote a generator comprehension instead of a list comprehension
What georg said, or you can return the iter of that list
def mylist():
mylist = []
for i in range(5):
mylist.append(myobject(i))
return iter(mylist)
probably not a good idea to use your function name as a variable name, though :)
llist = [0,4,5,6]
ii = iter(llist)
while (True):
try:
print(next(ii))
except StopIteration:
print('End of iteration.')
break
Related
I am trying to create python classes from a program that I wrote in functions only. So the code is working fine with functions but giving an error/errors when I convert the code to python classes.
Functions:
def eachList(index):
Series=df_ideal.iteritems()
bigList = []
for (columnName,item) in Series:
bigList.append(item)
return bigList[index]
def isResult(listing):
mylist=[]
for i in range(len([column_name for column_name in df_ideal])):
result = map(lambda x:x*x,residual(listing,eachList(i)))
Sum = int(sum(result))
mylist.append(Sum)
return(mylist)
With Classes
class Task1:
def __init__(self,train,ideal):
self.train = train
self.ideal = ideal
def __str__(self):
return self.ideal[index]
def residual(self,lst1,lst2):
self.subtracted = []
for item1,item2 in zip(lst1,lst2):
self.subtracted.append(item1-item2)
return self.subtracted
def eachList(self,index):
Series=df_ideal.iteritems()
self.bigList = []
for (columnName,item) in Series:
self.bigList.append(item)
return self.bigList[index]
Inheritance
class Resultant(Task1):
def __init__(self,train,ideal):
super().__init__(train,ideal)
def isResult(self,listing):
mylist=[]
for i in range(len([column_name for column_name in df_ideal])):
result = map(lambda x:x*x,Task1.residual(listing,Task1.eachList(i)))
Sum = int(sum(result))
mylist.append(Sum)
return(mylist)
The error you're hitting (I'm guessing) is that you're calling Task1.residual as if it's a class method, when it's not:
result = map(lambda x:x*x,Task1.residual(listing,Task1.eachList(i)))
For this to work you should be calling self.residual.
However, I think it would be better to just have a simple set of functions, since there's nothing in the implementation of these functions that requires any kind of persistent state to be shared between them. The logic is simple enough that each could be written as a single list comprehension:
def eachList():
return [item for _columnName, item in df_ideal.iteritems()]
def residual(lst1, lst2):
return [item1 - item2 for item1, item2 in zip(lst1, lst2)]
def isResult(listing):
return [sum(x * x for x in residual(listing, item)) for item in eachList()]
In answering this question, I stumbled across some unexpected behavior:
from typing import List, Iterable
class Name:
def __init__(self, name: str):
self.name = name
def generator(lst: List[Name]) -> Iterable[str]:
lst_copy = lst.copy()
for obj in lst_copy:
yield obj.name
When modifying the list that is passed to the generator, even though a copy is made, changes to the original list are still reflected:
lst = [Name("Tom"), Name("Tommy")]
gen = generator(lst)
lst[0] = Name("Andrea")
for name in gen:
print(name)
Output:
Andrea
Tommy
Simply returning a generator expression works as expected:
def generator(lst: List[Name]) -> Iterable[str]:
return (obj.name for obj in lst.copy())
Output:
Tom
Tommy
Why doesn't the lst.copy() in the first generator function work as expected?
I think the behavior is best understood with the addition of some extra print statements:
def generator(lst: List[Name]) -> Iterable[str]:
print("Creating list copy...")
lst_copy = lst.copy()
print("Created list copy!")
for obj in lst_copy:
yield obj.name
lst = [Name("Tom"), Name("Tommy")]
print("Starting assignment...")
gen = generator(lst)
print("Assignment complete!")
print("Modifying list...")
lst[0] = Name("Andrea")
print("Modification complete!")
for name in gen:
print(name)
Notice that the copy does not happen at assignment time -- it happens after the list is modified!
Starting assignment...
Assignment complete!
Modifying list...
Modification complete!
Creating list copy...
Created list copy!
Andrea
Tommy
Nothing in the generator's body is executed until the for loop attempts to extract an element. Since this extraction attempt occurs after the list is mutated, the mutation is reflected in the results from the generator.
The body of a generator does not start executing until the first item is requested. So in this code:
def generator(lst: List[Name]) -> Iterable[str]:
lst_copy = lst.copy()
for obj in lst_copy:
yield obj.name
lst = [Name("Tom"), Name("Tommy")]
gen = generator(lst)
lst[0] = Name("Andrea")
for name in gen:
print(name)
... First, the lst[0] = Name("Andrea") is executed. Then, you have a for loop, which starts executing the generator. That's when lst_copy = lst.copy() is executed, which is too late to get in before the lst[0] assignment.
The generator expression works, because the iterable portion of the generator (lst.copy(), the last part) must be evaluated before creating the iterator.
I want to change this
def has_class_but_no_id(tag):
return tag.has_key('class') and not tag.has_key('id')
This function is from Python2 not for Python3
I had idea that
I changed this HTML document in a list like this
list_of_descendants = list(soup.descendants)
So I can get tags which contain class but don't id
it is about that find all tags with class = blabla... but not id = ....
I have no idea how I can handle this problem
The documentation says:
I renamed one method for compatibility with Python 3:
Tag.has_key() -> Tag.has_attr()
Also, the exact same function is available in the documentation here:
If none of the other matches work for you, define a function that
takes an element as its only argument. The function should return True
if the argument matches, and False otherwise.
Here’s a function that returns True if a tag defines the “class”
attribute but doesn’t define the “id” attribute:
def has_class_but_no_id(tag):
return tag.has_attr('class') and not tag.has_attr('id')
Hey i solve this Problem.
What i had to do is
1.collect all the tags(BeautifulSoup) and all children of tags (contents)
soup = BeautifulSoup(html_doc,"html.parser")
list_of_descendants = list(soup.descendants)
2.eliminate all NavigableStrings(cuz they can't accept has_attr() Methodes)
def terminate_navis(list_of_some):
new_list = []
for elem in list_of_some:
if type(elem) == bs4.element.Tag:
new_list.append(elem)
else :
continue
return new_list
new_list = terminate_navis(list_of_descendants)
def contents_adding(arg_list):
//this Method helps that get all the childrens of tags in lists again
new_list = arg_list
child_list = []
for elem in arg_list:
if elem.contents:
child_list = elem.contents
child_list = terminate_navis(child_list)
new_list.extend(child_list)
new_list = list(set(new_list))
return new_list
3.filter all tags if they have attribute 'class' (has_attr) and if they don't have 'id'(also with has_attr)
def justcl(tag_lists):
class_lists = []
for elem in tag_lists:
if elem.has_attr('class'):
class_lists.append(elem)
else :
continue
return class_lists
def notids(class_lists):
no_id_lists = []
for elem in class_lists:
if elem.has_attr('id'):
continue
else :
no_id_lists.append(elem)
return no_id_lists
all this collected tags create as a list and print on the screen
print or using for loop and so on...
Ok so I got this code :
class ApiCall(object):
def __init__(self, url):
self.url = url
def call(self):
call = requests.get(self.url)
response = call.content.decode('utf-8')
result = json.loads(response)
return result
class IncomeSources(object):
def __init__(self, result):
self.result = result
def all(self):
#This is the dict comprehension
#return {(slot['accountLabelType'], slot['totalPrice']) for slot in self.result}
for slot in self.result:
return (slot['accountLabelType'], slot['totalPrice'])
def main():
url = ('https://datafeed/api/')
api_result = ApiCall(url).call()
target = IncomeSources(api_result).all()
print(target)
main()
The result with a regular for on a function, returns this wich is not desired, as it only returns the pair of the first object :
('Transport', 888)
But with the dict comprehension, it returns all slot pairs of all the json objects on that json response ( that is cool ) Why the dict comprehension grabs all the pairs and the regular for is not ?
Why the dict comprehension grabs all the pairs and the regular for is not ?
What happens when you loop over something and have a return statement in the loop is that as soon as a return statement is encountered, that value (and only that value) is returned.
The dict comprehension first constructs the entire dictionary which then gets returned as a whole to the caller.
This has less to do with the comprehension and more with the return statement. Compare:
>>> def foo():
... for i in range(5):
... return i
...
>>> foo()
0
With:
>>> def foo():
... return list(range(5))
...
>>> foo()
[0, 1, 2, 3, 4]
I am really new to python, so this might be really easy.
I want to print two strings defined in a class as static members with a class method that yields each string.
This is a simplified version of what I am trying to do:
#!/usr/bin/python
import sys
class test:
str1 = "Hello"
str2 = "World\n" #"\n" is needed for the example
def printMe(self):
yield test.str1
yield test.str2
hello = test()
print "Testing initiated:"
sys.stdout.write(hello.printMe())
sys.stdout.write(hello.printMe())
This is the output:
sys.stdout.write(hello.printMe()) TypeError: expected a character
buffer object
You are attempting to use a generator function, read about the yield keyword here
import sys
class Test:
def __init__(self): # it's possible to initialise these attributes in the __init__ method, so they are created on class instantiation(when you did hello = Test())
self.str1 = "Hello"
self.str2 = "World\n" #"\n" is needed for the example
def printMe(self):
for i in [self.str1, self.str2]:
yield i
app = Test()
print "Testing initiated:"
for i in app.printMe():
print i # is there a reason why you can't use print?
If however you want to print the lines one at a time, at specific points in the code, like in your loop you mentioned in the comment:
gen = app.printMe()
then every time you want to print:
gen.next()
this triggers the next yield statement. The generator function effectively 'holds'/remembers it's place until you call next again, until all the yield statements have been yielded.
You should do something like this
for line in hello.printMe():
print line
But really there are a lot of easier ways than using yield statements.
using yield turns your function into a generator. If this is really what you want, you will need to iterate over the generator to get the values:
gen = hello.printMe()
sys.stdout.write(gen.next())
sys.stdout.write(gen.next())
or better:
for prop in hello.printMe():
sys.stdout.write(prop)
Your printMe method is a generator function, which returns an iterable. You need to iterate over it to get the results :
for item in hello.printMe():
print item
You can do this, but I'm using print, hope this helps you:
class test:
str1 = "Hello"
str2 = "World\n" #"\n" is needed for the example
def printMe(self):
yield test.str1
yield test.str2
hello = test()
print "Testing initiated:"
out = hello.printMe()
print(out.next(),end=' ')
print(out.next(),end=' ')