3

I want to know which of them is more pythonic/efficient.

A)

HostName = [item.lstrip().strip('.').encode('ascii') for item in HostName]

B)

for i in range(len(HostName)):
   HostName[i] = HostName[i].lstrip()
   HostName[i] = HostName[i].strip('.')
   HostName[i] = HostName[i].encode('ascii')
  • A is more Pythonic. As for efficiency, I doubt the difference is significant. – ggorlen Jul 27 '18 at 07:16
  • 1
    `HostName[i] = HostName[i].lstrip().strip('.').encode('ascii')` would be a more fair comparison - there's certainly no reason to do three separate assignments when you don't care about the intermediate results. – dimo414 Jul 27 '18 at 07:49
  • Option A is better and more pythonic. First one is using python list comprehensions. List comprehensions aren’t the only way to work on lists. Various built-in functions and lambda functions can create and modify lists in fewer lines of code with an efficient way. as well as more pythonic way – Projesh Bhoumik Jul 27 '18 at 07:53
  • Check this post. https://stackoverflow.com/questions/51526242/why-do-two-identical-lists-have-a-different-memory-footprint – Marcus.Aurelianus Jul 27 '18 at 08:18

2 Answers2

2

This Can be known by using Python Disassemble, dis module.

import dis

https://docs.python.org/2/library/dis.html

a = "Hello"
def inline(a):
  b = [_ for _ in a]
  return b

def regular(a):
  c = []
  for _ in a:
    c.append(_)
  return c

#

dis.dis(inline)
2           0 LOAD_CONST               1 (<code object <listcomp> at 0x00000292223534B0, file "<stdin>", line 2>)
              2 LOAD_CONST               2 ('f.<locals>.<listcomp>')
              4 MAKE_FUNCTION            0
              6 LOAD_FAST                0 (a)
              8 GET_ITER
             10 CALL_FUNCTION            1
             12 STORE_FAST               1 (b)

  3          14 LOAD_FAST                1 (b)
             16 RETURN_VALUE

#

dis.dis(regular)
  2           0 BUILD_LIST               0
              2 STORE_FAST               1 (c)

  3           4 SETUP_LOOP              22 (to 28)
              6 LOAD_FAST                0 (a)
              8 GET_ITER
        >>   10 FOR_ITER                14 (to 26)
             12 STORE_FAST               2 (x)

  4          14 LOAD_FAST                1 (c)
             16 LOAD_ATTR                0 (append)
             18 LOAD_FAST                2 (x)
             20 CALL_FUNCTION            1
             22 POP_TOP
             24 JUMP_ABSOLUTE           10
        >>   26 POP_BLOCK

  5     >>   28 LOAD_FAST                1 (c)
             30 RETURN_VALUE

For the same operation, you could see the number of operations being performed.

Surya Tej
  • 1,342
  • 2
  • 15
  • 25
0

The first one is inherently better. It depends on the implementation and platform, but the first one allows the python core to do the instructions in a parallel manner. The second one is an iteration that is much harder for the runtime engine to do in parallel. Some C and C++ compilers are able to detect even these cases, but I don't think it is possible to do so in python. As far as I remember, the marshal byte code that python generated used to have no such optimizations a few years ago when I was into that.

The main difference is between the functional programming and sequential programming. On current platforms and with today's CPUs, I believe it can be much more efficient to use functional programming paradigm when it makes sense.

In brief, the mantra is don't decide for the things the compiler or runtime can decide better than you. You may not be able to tell whether your target is able to do parallel calculations, but the runtime can, and this way you allow it to choose.

Also the first one has a pythonic thinking. And also, it is more readable in my opinion.

arashka
  • 1,226
  • 3
  • 17
  • 30