Compressing “n”-time object member call

心已入冬 提交于 2019-12-23 18:47:00

问题


Is there any non-explicit for way to call a member n times upon an object?

I was thinking about some map/reduce/lambda approach, but I couldn't figure out a way to do this -- if it's possible.

Just to add context, I'm using BeautifulSoup, and I'm extracting some elements from an html table; I extract some elements, and then, the last one.

Since I have:

# First value
print value.text

# Second value
value = value.nextSibling
print value.text

# Ninth value
for i in xrange(1, 7):
    value = value.nextSibling
print value.text

I was wondering if there's any lambda approach -- or something else -- that would allow me to do this:

# Ninth value
((value = value.nextSibling) for i in xrange(1, 7))
print value.text

P.S.: No, there's no problem whatsoever with the for approach, except I really enjoy one-liner solutions, and this would fit really nice in my code.


回答1:


I have a strong preference for the loop, but you could use reduce:

>>> class Foo(object):
...     def __init__(self):
...        self.count = 0
...     def callme(self):
...        self.count += 1
...        return self
... 
>>> a = Foo()
>>> reduce(lambda x,y:x.callme(),range(7),a)
<__main__.Foo object at 0xec390>
>>> a.count
7



回答2:


You want a one-liner equivalent of this:

for i in xrange(1, 7):
    value = value.nextSibling

This is one line:

for i in xrange(1, 7): value = value.nextSibling

If you're looking for something more functional, what you really want is a compose function, so you can compose callme() (or attrgetter('my_prop'), or whatever) 7 times.




回答3:


In case of BS you can use nextSiblingGenerator() with itertools.islice to get the nth sibling. It would also handle situations where there is no nth element.

from itertools import islice

nth = 7
next(islice(elem.nextSiblingGenerator(), nth, None), None)



回答4:


Disclaimer: eval is evil.

value = eval('value' + ('.nextSibling' * 7))



回答5:


Ah! But reduce is not available in Python3, at least not as a built in.

So here is my try, portable to Python2/3 and based on the OP failed attempt:

[globals().update(value=value.nextSibling) for i in range(7)]

That assumes that value is a global variable. If value happens to be a member variable, then write instead:

[self.__dict__.update(value=value.nextSibling) for i in range(7)]

You cannot use locals() because the list comprehension creates a nested local scope, so the real locals() is not directly available. However, you can capture it with a bit of work:

(lambda loc : [loc.update(x=x.nextSibling) for i in range(7)])(locals())

Or easier if you don't mind duplicating the number of lines:

loc = locals()
[loc.update(value=value.nextSibling) for i in range(7)]

Or if you really fancy one-liners:

loc = locals() ; [loc.update(value=value.nextSibling) for i in range(7)]

Yes, Python can use ; too 8-)

UPDATE:

Another fancy variation, now with map instead of the list comprehension:

list(map(lambda d : d.update(value=value.nextSibling), 7 * [locals()]))

Note the clever use of list multiplication to capture the current locals() and create the initial iterable at the same time.




回答6:


The most direct way to write it would be:

value = reduce(lambda x, _: x.nextSibling, xrange(1,7), value)


来源:https://stackoverflow.com/questions/14635914/compressing-n-time-object-member-call

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!