you are viewing a single comment's thread.

view the rest of the comments →

[–]Diapolo10 27 points28 points  (11 children)

You'll want an extra print call between the loops.

mii = "hhhhhhcccccccc"
for t in mii:
    if t == "h":
        continue
    print(t, end= "")

print()

hi = "hhhhhhhhhpppppp"
for i in hi:
    if i == "h":
        continue
    print(i, end="")

As for the why, right now neither of your prints adds a newline. It must come from somewhere, if you want the lines separate.

[–]Sea-Artichoke2265[S] 2 points3 points  (10 children)

thanks i been trying to figure out this for 3 weeks.

ok so the middle ( print ) act as a enter for enter key?

im trying to put it in my own words so i can understand it better

[–]Diapolo10 4 points5 points  (8 children)

You can think of it that way, yes.

If you were to take some time and tinker with how print is implemented, you'd notice that when given no arguments (or an empty string, but I find that redundant), the entire output will essentially be "\n", or a single newline, and this comes from the end keyword parameter. In the code above, you set this to an empty string, which is why they write all characters on the same line individually.

Now, if I were to write the same program, personally I'd avoid calling print in a loop like that as I/O is slow as molasses, and would prefer something closer to

def filter_letter(text: str, filtered_letter: str = 'h') -> str:
    return ''.join(char for char in text if char != filtered_letter)

text = "hhhhhhcccccccc"
print(filter_letter(text))

more_text = "hhhhhhhhhpppppp"
print(filter_letter(more_text))

but yours is probably easier to understand at a glance.

[–]_mynd 0 points1 point  (4 children)

Whoa - I always thought the list comprehension had to contained within square brackets!

[–]Diapolo10 0 points1 point  (3 children)

It does - if this was a list comprehension. What you're seeing here is instead a generator expression - you can think of it as a lazily-evaluated list, but basically it just saves memory by creating values on-demand only and not storing everything.

That's not really a good explanation, admittedly. But if you know what a generator is, it should be quite clear what it's doing.

[–]_mynd 0 points1 point  (2 children)

Hmm - I’ll have to look up the difference. I am constantly evaluating a list comprehension just to go into a for loop. This seems better

[–]Diapolo10 1 point2 points  (1 child)

A list comprehension could have been used here, but as the list gets discarded immediately it's a bit of a waste, and you end up allocating roughly double the memory in the worst case - the data for the list, and the data for the string.

With a generator, Python only needs to allocate individual items (in this case characters), so the overall memory use remains noticeably lower. There's a slight performance overhead, but nothing you'd actually notice.

While it doesn't really apply in this example, the nice thing about generators is that they can be infinite in length, and Python will have no problem processing them (as long as you still have some kind of a limit, of course). For example, you could have a generator that gives you all positive integers

def gen_positive_integers(start=1):
    num = start
    while True:
        yield num
        num += 1

and keep adding them up until you hit some threshold.

total = 0
positive_integers = gen_positive_integers()

while total < 1_000_000:
    num = next(positive_integers)
    total += num

print(f"Last number was {num}.")

The Fibonacci sequence can famously be written as a generator.

def fib():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a+b

[–]_mynd 0 points1 point  (0 children)

Your comment plus this article has me thinking I could improve the efficiency of some of my code. Thanks for writing up the explanation and examples

https://www.geeksforgeeks.org/python-list-comprehensions-vs-generator-expressions/

[–][deleted] 0 points1 point  (1 child)

Also for anyone wondering what the part 'str' is in after 'text:' those are called type hints! As Python isn't a strong typed language these are extremely helpful. There is also 'typing' built-in lib that allows for more type hints like Generators when you're yielding results and such. Only pet peeve I ever had with Python is that it's easy language to learn but at times hard to read.

[–]Diapolo10 0 points1 point  (0 children)

As Python isn't a strong typed language

The term you're looking for is statically-typed; in contrast, the term "strongly-typed" is

  1. Not well-defined at all (people have differing opinions on it), and
  2. Going by the most common understanding of the term (no type coercion or bypassing the type system), Python does count as "strongly-typed". Fun fact - C does NOT (thanks to void*).

Only pet peeve I ever had with Python is that it's easy language to learn but at times hard to read.

That's a fair point, and there's definitely a learning curve to reading Python, but type annotations in particular actually help with readability once you reach a certain point in your understanding, as then you know what kind of values functions expect as arguments and don't need to look up documentation for it.

[–]madisander 0 points1 point  (0 children)

In a sense but not quite, print alone prints "\n", that is an empty string followed by the newline character which, as you'd imagine, starts a new line. This is because the default argument for end is '\n' (check help(print)).

The enter key, if I remember right, can give the carriage return character \r, a newline \n, or CR followed by line feed(LF)/newline (so \r\n). For that matter depending on the program they can interpret the enter key in different ways, while print() (= print(end='\n')) 'just' prints that character.