Posts: 3,458
Threads: 101
Joined: Sep 2016
How do you guys do method chaining? Everything I've tried to break method calls across multiple lines looks pretty ugly.
Or do you guys just not do chaining at all?
class query(object):
def __init__(self, source):
self.filters = []
self.source = source
def where(self, filter):
self.filters.append(filter)
return self
def get(self):
all_filters = lambda x: all(func(x) for func in self.filters)
return list(filter(all_filters, self.source))
things = query(range(1000)
).where(lambda x: x % 2 == 0
).where(lambda x: x % 3 == 0
).where(lambda x: x % 4 == 0
).where(lambda x: x % 5 == 0)
#things = query(range(1000)).where(
# lambda x: x % 2 == 0).where(
# lambda x: x % 3 == 0).where(
# lambda x: x % 4 == 0).where(
# lambda x: x % 5 == 0)
print(things.get()) Output: [0, 60, 120, 180, 240, 300, 360, 420, 480, 540, 600, 660, 720, 780, 840, 900, 960]
Posts: 591
Threads: 26
Joined: Sep 2016
Not quite sure if this is what you are going for but it gives the same results:
from functools import partial
# ... class definition
def divisible_by(div, num):
return num % div == 0
things = query(range(1000))
for i in (2,3,4,5):
things.where(partial(divisible_by, i)) Or even:
from functools import partial
# ... class definition
def divisible_by(div, num):
return num % div == 0
things = query(range(1000))
things.filters = [partial(divisible_by, i) for i in (2,3,4,5)]
Posts: 3,458
Threads: 101
Joined: Sep 2016
Yeah, that gives the same results, but it was just a toy class to demonstrate where it could syntactically be useful.
What if each filter was unrelated from the others? Like, what if it's a wrapper around BeautifulSoup, and each query is a sub-selector of the parent filter's result?
The original example could also be written like this, though I think using backslashes looks worse than the dangling parentheses...
things = query(range(1000)) \
.where(lambda x: x % 2 == 0) \
.where(lambda x: x % 3 == 0) \
.where(lambda x: x % 4 == 0) \
.where(lambda x: x % 5 == 0)
Posts: 566
Threads: 10
Joined: Apr 2017
(May-01-2017, 02:01 PM)nilamo Wrote: The original example could also be written like this, though I think using backslashes looks worse than the dangling parentheses...
things = query(range(1000)) \
.where(lambda x: x % 2 == 0) \
.where(lambda x: x % 3 == 0) \
.where(lambda x: x % 4 == 0) \
.where(lambda x: x % 5 == 0) Actually - while I hate backlashes - I would rather use this option - it's ugly but it's more obvious
Test everything in a Python shell (iPython, Azure Notebook, etc.) - Someone gave you an advice you liked? Test it - maybe the advice was actually bad.
- Someone gave you an advice you think is bad? Test it before arguing - maybe it was good.
- You posted a claim that something you did not test works? Be prepared to eat your hat.
Posts: 3,458
Threads: 101
Joined: Sep 2016
May-01-2017, 04:34 PM
(This post was last modified: May-01-2017, 04:35 PM by nilamo.)
If we rewrite the original class to be immutable (...as was my initial intention, but I wrote it fast), it becomes a little more clear why I'm aiming for this.
I guess using temporary variables or just having multiple filters/selectors on a single line is the way to go.
Here's probably a better example:
class query(object):
def __init__(self, source, filters = ()):
self.filters = filters
self.source = source
def where(self, filter):
filters = self.filters + (filter, )
new_query = query(self.source, filters)
return new_query
def __and__(self, other):
filters = self.filters + other.filters
return query(self.source, filters)
def get(self):
all_filters = lambda x: all(func(x) for func in self.filters)
return list(filter(all_filters, self.source))
things = query(range(1000))
by_9 = things.where(lambda x: x % 9 == 0)
only_odd = things.where(lambda x: x % 2 != 0)
further_filtered = only_odd.where(lambda x: x % 3 == 0).where(lambda x: x % 7 == 0)
print((further_filtered & by_9).get())
# [63, 189, 315, 441, 567, 693, 819, 945]
Posts: 591
Threads: 26
Joined: Sep 2016
May-01-2017, 10:19 PM
(This post was last modified: May-01-2017, 10:19 PM by Mekire.)
Yes, as to your original lambda chaining; I would absolutely never do that and it makes my eyes bleed, if that is the answer you were looking for. That is why I proposed an alternative. xD
If a library I was using forced me to code this way I would really try to find an alternative library as well.
|