[Python] Waarom "Out of range"?

Pagina: 1
Acties:

Onderwerpen

Vraag


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Mijn vraag

Hallo, ik probeer het volgende vraagstuk te programmeren in python:

Gegeven een willekeurige string, bepaal hoeveel letters hoofdletter zijn en bepaal hoeveel letter kleine letter zijn.


Wat ik al gevonden of geprobeerd heb

Python:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 def function(string):
    upper = 0
    lower = 0
    count = 0
    
    for x in string:
        while count < len(string):
            if x[count].isupper() == True:
                upper += 1
                count += 1
            elif x[count].islower() == True:
                lower += 1
                count += 1
            else:
                count += 1
    return upper and lower

print(function(" Hello people"))


string = "Hello Everybody"
print(function(string))


Ik heb als eerste een functie gedefinieerd. Vervolgens laat ik het script elk character apart bekijken. Daarna stel ik als voorwaarde dat de count(index van de string) kleiner moet zijn dan de getalslengte van de string. Tenslotte probeer ik mijn doel te bereiken door if-else statements.

Ik krijg als foutmelding dat de index buiten de range ligt, maar ik snap niet helemaal hoe dat kan.

Zou iemand eens naar mijn code kunnen kijken? _/-\o_

[ Voor 0% gewijzigd door RobIII op 11-07-2019 14:29 . Reden: Coe tags toegevoegd ]

Alle reacties


Acties:
  • 0 Henk 'm!

  • Aragnut
  • Registratie: Oktober 2009
  • Laatst online: 15:43
Zet de code even in code block neer. Zeker bij python is het behoud van layout van belang.

Acties:
  • 0 Henk 'm!

  • Nilltris
  • Registratie: Mei 2011
  • Laatst online: 14-06-2023
Ik doe zelf niets in python maar is je while loop niet overbodig?

Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Heb je al gedebugged? Debuggen: Hoe doe ik dat?

Verder: als je voortaan code post, gebruik dan code tags a.u.b. ;)

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

  • Invisible_man
  • Registratie: Juni 2006
  • Laatst online: 16:08
Je hebt nu zowel een for lus als een while lus, dus je loopt de lengte van de string in het kwadraat maal de string door.

Acties:
  • 0 Henk 'm!

  • dev10
  • Registratie: April 2005
  • Laatst online: 09-09 15:21
De out out range krijg je, omdat je probeert een item uit een array te vissen die niet bestaat. De enige plek waar je dat doet, is in de while-loop en zoals @Nilltris zegt, die heb je helemaal niet nodig. ;)

Met een klein beetje debuggen, zoals @RobIII zegt, moet je er dan uit kunnen komen.

[ Voor 29% gewijzigd door dev10 op 11-07-2019 14:30 ]


Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Los van wat anderen al tippen: in alle 3 de 'takken' van je if doe je een count += 1. Dan kun je die natuurlijk net zo goed erbuiten zetten. Maar zoals gezegd, je while is niet nodig en daarmee je count ook niet.

Verder ben ik geen python expert, maar:
Python:
1
return upper and lower
Euh, kan dat :? (Antwoord: nee dus :P)
Is het niet:
Python:
1
return (upper, lower)
ofzoiets? (Antwoord: ja dus :P )

"and" doet iets anders dan jij denkt ;)

[ Voor 75% gewijzigd door RobIII op 11-07-2019 14:59 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 01:28

.oisyn

Moderator Devschuur®

Demotivational Speaker

Invisible_man schreef op donderdag 11 juli 2019 @ 14:30:
Je hebt nu zowel een for lus als een while lus, dus je loopt de lengte van de string in het kwadraat maal de string door.
2n hooguit, count zal na de 2e iteratie van de for-lus al gelijk zijn aan de lengte van de string, dus de while wordt niet meer uitgevoerd.

Het echte probleem is dat hij in x probeert te indexeren, terwijl x al een enkel karakter uit de string voorstelt.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 12:01
Je verwijst beide keren naar dezelfde uitwerking ;)

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
CurlyMo schreef op donderdag 11 juli 2019 @ 14:58:
[...]

Je verwijst beide keren naar dezelfde uitwerking ;)
Nietus :+
(M.a.w. fixed. Thanks :Y) )

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

  • RzrBck
  • Registratie: Juni 2011
  • Laatst online: 12-09 12:22
Dit kan ook:
Python: voorbeeld
1
2
3
4
5
6
7
8
9
10
11
12
import itertools

def teller(string):
    up = itertools.count(0)
    lo = itertools.count(0)
    [(next(up)) for i in string if i.isupper()]
    [(next(lo)) for i in string if i.islower()]
    
    return up, lo

string = 'Hello People'
print(teller(string))


Het voordeel is dat je geen for- of while-lussen gebruikt, maar list-comprehention. Daar zitten in python allerlei voordelen aan qua efficiëntie als ik me niet vergis.
Meer info: https://www.pythonforbegi...-comprehensions-in-python

Acties:
  • +1 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 12:01
RzrBck schreef op donderdag 11 juli 2019 @ 15:03:
Het voordeel is dat je geen for- of while-lussen gebruikt, maar list-comprehention. Daar zitten in python allerlei voordelen aan qua efficiëntie als ik me niet vergis.
Wat is dat dan op regel 6 en 7 ;)

Overigens zorgen deze trucjes ervoor dat niemand je begrijpt behalve Python programmeurs.

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • 0 Henk 'm!

  • RzrBck
  • Registratie: Juni 2011
  • Laatst online: 12-09 12:22
CurlyMo schreef op donderdag 11 juli 2019 @ 15:05:
[...]

Wat is dat dan op regel 6 en 7 ;)

Overigens zorgen deze trucjes ervoor dat niemand je begrijpt behalve Python programmeurs.
:P

De bewoording "dit kan ook" was wel bewust zo gekozen.
En dat het minder duidelijk/alleen voor python programmeurs te begrijpen is.. weet ik niet.

Het is denk ik een van de dingen die python heel mooi maken, en juist een sterk punt van python boven efficiëntere / andere talen.

Acties:
  • +1 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
RzrBck schreef op donderdag 11 juli 2019 @ 15:12:
en juist een sterk punt van python boven efficiëntere / andere talen.
En precies waarom @CurlyMo zei "dat niemand je begrijpt behalve Python programmeurs".

Los daarvan: het is hier niet de bedoeling om kant-en-klare antwoorden voorgekauwd-en-wel weg te geven. Je kunt prima TS in de juiste richting helpen, maar probeer 't geven van complete antwoorden voortaan achterwege te laten a.u.b:
Give a man a fish and feed him for a day. Teach a man how to fish and feed him for a lifetime.

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • +1 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 12:01
RzrBck schreef op donderdag 11 juli 2019 @ 15:12:
[...]
Het is denk ik een van de dingen die python heel mooi maken, en juist een sterk punt van python boven efficiëntere / andere talen.
Ik snap het punt wel dat je probeert te maken, maar je maakt hem niet ;)

Je voorbeeld gebruikt namelijk nog steeds standaard loops waardoor er ook nog steeds de python interpreter de vertragende factor is.

Hier een mooi artikel van Guido van Rossum zelf over wat jij probeert te illustreren;
https://www.python.org/doc/essays/list2str/

TLDR:
Python:
1
2
3
import array
    def f7(list):
        return array.array('B', list).tostring()

Is ~ 24 keer zo snel als:
Python:
1
2
3
4
5
def f3(list):
        string = ""
        for character in map(chr, list):
            string = string + character
        return string


En in navolging van @RobIII een quote:
We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • 0 Henk 'm!

  • Morrar
  • Registratie: Juni 2002
  • Laatst online: 08:54
Hoop appels en peren in dat artikel o.a. vanwege namespace lookups en lambda/ function compilatie...

List comprehensions zijn wel degelijk veel sneller in Python dan loops, met name omdat ze makkeljik te unrollen zijn. Als je er eenmaal aan gewend bent, zijn ze ook niet lastig te lezen.

Testje:
Python:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def count_upper_loop(string):
    up = 0
    lo = 0
    for c in string:
        if c.isupper():
            up += 1
        elif c.islower():
            lo += 1
        return up, lo
    return up, lo

def count_upper(string):
    up = sum([c.isupper() for c in string])
    lo = sum([c.islower() for c in string])
    return up, lo

# 3.29 µs ± 41.4 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit count_upper("THIS IS not all CAPS!")

# 171 ns ± 1.73 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
%timeit count_upper_loop("THIS IS not all CAPS!")


Zeker geen overbodige optimalisatie dus!

[ Voor 4% gewijzigd door Morrar op 18-07-2019 09:13 ]


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 01:28

.oisyn

Moderator Devschuur®

Demotivational Speaker

Nou ben ik wel benieuwd of sum een in Python gedefinieerde functie is.

.edit: wacht even. Volgens die code is de bovenste implementatie dus sneller? :D Ook heb ik een beetje een probleem met die 171ns, dat lijkt me te absurd snel.

[ Voor 53% gewijzigd door .oisyn op 18-07-2019 09:49 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Wolfboy
  • Registratie: Januari 2001
  • Niet online

Wolfboy

ubi dubium ibi libertas

Morrar schreef op donderdag 18 juli 2019 @ 09:12:
Testje:
Zeker geen overbodige optimalisatie dus!
Ja, nogal wiedes dat die snel is. Je geeft bij het eerste resultaat direct terug :P

Hier is de fixed versie:

Python:
1
2
3
4
5
6
7
8
9
10
def count_upper_loop(string):
    up = 0
    lo = 0
    for c in string:
        if c.isupper():
            up += 1
        elif c.islower():
            lo += 1
        # return up, lo
    return up, lo
.oisyn schreef op donderdag 18 juli 2019 @ 09:43:
Nou ben ik wel benieuwd of sum een in Python gedefinieerde functie is.
Ja, sum zit in de core en standaard in je scope: https://docs.python.org/3/library/functions.html#sum

Ik heb nog een ander voorstel:
Python:
1
2
3
4
5
6
7
import string

def function(input_string):
   input_set = set(input_string)
   upper = input_set & set(string.ascii_uppercase)
   lower = input_set & set(string.ascii_lowercase)
   return len(upper), len(lower)


In [11]: %timeit count_upper_loop("THIS IS not all CAPS!")
The slowest run took 12.47 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 2.05 µs per loop

In [12]: %timeit count_upper("THIS IS not all CAPS!")
100000 loops, best of 3: 4.16 µs per loop

In [13]: %timeit function("THIS IS not all CAPS!")
The slowest run took 4.55 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 2.67 µs per loop

[ Voor 75% gewijzigd door Wolfboy op 18-07-2019 10:12 . Reden: Kleine optimalisatie en len() + benchmarks ]

Blog [Stackoverflow] [LinkedIn]


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 01:28

.oisyn

Moderator Devschuur®

Demotivational Speaker

Hiermee zeg je nogmaals dat de for-loop dus sneller is dan de variant met sum. Realiseer je dat wel?

[ Voor 8% gewijzigd door .oisyn op 18-07-2019 10:06 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Wolfboy
  • Registratie: Januari 2001
  • Niet online

Wolfboy

ubi dubium ibi libertas

.oisyn schreef op donderdag 18 juli 2019 @ 10:05:
Hiermee zeg je nogmaals dat de for-loop dus 171ns is en de variant met sum 3us. Realiseer je dat wel?
Oh... wow. Je hebt gelijk, verkeerd gelezen 8)7

Nee, die kan ik ook echt niet verklaren


[edit] Gevonden, die code klopt gewoon niet :P

@.oisyn Er zit een return _in_ de for loop, dat maakt hem wel wat sneller ;)

[ Voor 18% gewijzigd door Wolfboy op 18-07-2019 10:13 ]

Blog [Stackoverflow] [LinkedIn]


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 01:28

.oisyn

Moderator Devschuur®

Demotivational Speaker

@Wolfboy Ja dan snap ik de ~200ns ook meer :P. Maar het is dus nog steeds sneller?

[ Voor 7% gewijzigd door .oisyn op 18-07-2019 10:15 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • +1 Henk 'm!

  • Wolfboy
  • Registratie: Januari 2001
  • Niet online

Wolfboy

ubi dubium ibi libertas

.oisyn schreef op donderdag 18 juli 2019 @ 10:15:
@Wolfboy Ja dan snap ik de ~200ns ook meer :P. Maar het is dus nog steeds sneller?
Argh, ik haal ze iedere keer door de war. Lang leve duidelijke functienamen.

Zo te zien nogsteeds de snelste ja.

Hier is optie 4 en 5 met dezelfde tijds-schaal:

Python:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def count_map(s):
    up = sum(map(str.isupper, s))
    lo = sum(map(str.islower, s))
    return up, lo

def count_sum(s):
    up = sum(c.isupper() for c in s)
    lo = sum(c.islower() for c in s)
    return up, lo

In [28]: %timeit count_map("THIS IS not all CAPS!")
100000 loops, best of 3: 3.65 µs per loop

In [41]: %timeit count_sum("THIS IS not all CAPS!")
100000 loops, best of 3: 4.46 µs per loop

[ Voor 27% gewijzigd door Wolfboy op 18-07-2019 10:35 ]

Blog [Stackoverflow] [LinkedIn]


Acties:
  • +1 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 01:28

.oisyn

Moderator Devschuur®

Demotivational Speaker

Dus de conclusie is: for-loops zijn sneller dan list comprehension. Duidelijk :Y)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • +1 Henk 'm!

  • Merethil
  • Registratie: December 2008
  • Laatst online: 14:48
.oisyn schreef op donderdag 18 juli 2019 @ 11:03:
Dus de conclusie is: for-loops zijn sneller dan list comprehension. Duidelijk :Y)
Is dat niet gewoon omdat je bij de eerste optie (for-loop) eenmaal door de lijst van karakters heenloopt terwijl die sums ook een loop doen en dus tweemaal door de lijst heen loopt? (Want één sum voor upper en één sum voor lower). Lijkt me toch vrij normaal? :P

Acties:
  • 0 Henk 'm!

  • Wolfboy
  • Registratie: Januari 2001
  • Niet online

Wolfboy

ubi dubium ibi libertas

Merethil schreef op donderdag 18 juli 2019 @ 11:10:
[...]


Is dat niet gewoon omdat je bij de eerste optie (for-loop) eenmaal door de lijst van karakters heenloopt terwijl die sums ook een loop doen en dus tweemaal door de lijst heen loopt? (Want één sum voor upper en één sum voor lower). Lijkt me toch vrij normaal? :P
Dat speelt ook mee maar het lijkt niet de enige factor te zijn

Python:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
In [1]: test_string = 'THIS IS not all CAPS!'

In [2]: def count_upper_for_loop(s):
   ...:     up = 0
   ...:     for c in s:
   ...:         if c.isupper():
   ...:             up += 1
   ...:     return up
   ...:

In [3]: def count_upper_list_comprehension(s):
   ...:     return sum([c.isupper() for c in s])
   ...:

In [4]: def count_upper_generator(s):
   ...:     return sum(c.isupper() for c in s)
   ...:

In [5]: def count_upper_list_comprehension_filtered(s):
    ...:     return sum([1 for c in s if c.isupper()])
    ...:

In [6]: def count_upper_generator_filtered(s):
    ...:     return sum(1 for c in s if c.isupper())
    ...:

In [7]: %timeit count_upper_for_loop(test_string)
2.26 µs ± 39.3 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [8]: %timeit count_upper_list_comprehension(test_string)
3.06 µs ± 43.9 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [9]: %timeit count_upper_generator(test_string)
3.46 µs ± 49.7 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [10]: %timeit count_upper_list_comprehension_filtered(test_string)
2.71 µs ± 62 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [11]: %timeit count_upper_generator_filtered(test_string)
2.85 µs ± 43.6 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)


Al is de for loop van alle opties die ik getest heb wel de snelste.

Alternatieve opties:
Python:
1
2
sum(list(map(str.isupper, s)))
len(list(filter(str.isupper, s)))

Blog [Stackoverflow] [LinkedIn]


Acties:
  • 0 Henk 'm!

  • BernardV
  • Registratie: December 2003
  • Laatst online: 10:54
Het zal niet de snelste oplossing zijn, maar je kunt het ook nog met een regex replace doen:
code:
1
2
def count_upper_regex(s):
    return len(re.sub(r'[^A-Z]','',s))

Acties:
  • 0 Henk 'm!

  • Ben(V)
  • Registratie: December 2013
  • Laatst online: 17:38
https://towardsdatascienc...run-smoother-3cf8f87172ae

En het argument dat list comprehensions moeilijk te lezen is voor niet Python kenners is uiteraard onzin.
Code hoeft uiteraard alleen maar begrepen te worden door mensen die de taal gebruiken.

Met andere woorden, als je Python leest moet je Python kennen anders lijkt het me een vrij zinloze bezigheid.

[ Voor 254% gewijzigd door Ben(V) op 05-08-2019 14:14 ]

All truth passes through three stages: First it is ridiculed, second it is violently opposed and third it is accepted as being self-evident.


Acties:
  • 0 Henk 'm!

  • Sendy
  • Registratie: September 2001
  • Niet online
Ik zou in de eenvoudige loop dan ook het volgende vervangen
code:
1
2
      if c.isupper():
        up += 1

door
code:
1
       up += c.isupper()


Dan lijkt het nog net iets meer op elkaar.

Acties:
  • 0 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 12:01
Ben(V) schreef op maandag 5 augustus 2019 @ 12:46:
Met andere woorden, als je Python leest moet je Python kennen anders lijkt het me een vrij zinloze bezigheid.

Ik werk liever met collega's samen met een (programmeer)talenknobbel dan met Python savanten. Het begrijpen van de (generieke) concepten en ontwerpprincipes achter of van een programmeertaal vind ik vele malen belangrijker dan alleen die principes kennen die specifiek voor Python bedoel zijn. Door alleen hele Python specifieke dingen te doen, maak je de overdracht naar (toekomstige kundigere maar meer generieke) collega's onnodig complex en de pool van waaruit je toekomstige collega's kan invliegen onnodig klein.

Als je performance echt de bottleneck is, ok. Maar dat is 99% van de tijd niet zo, want anders had je ook geen Python gebruikt, maar C++ of Java o.i.d. :p

Ik heb namelijk iets te vaak dit gesprek (ik ben B):
A Ben jij bekent met taal X?
B Nee, maar beschrijf je probleem.
A Wat ik probeer te bereiken is [...], maar het project is in taal X.
B We bespreken oplossingsrichtingen onafhankelijk van de taal.
A Oplossingsrichting wordt uitgewerkt in taal X en werkt.

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • 0 Henk 'm!

  • Ben(V)
  • Registratie: December 2013
  • Laatst online: 17:38
Als je deze redenering volgt dan zou je enkel een hele kleine taal zoals C moeten gebruiken en dan ook van alle includes afblijven en alleen de basis control structuren inzetten.
En zelfs dan kan ik nog wel wat pointer naar pointer structuren bedenken waar de gemiddelde programmuur nog wel even z'n hoofd over breekt om te doorgronden wat er staat.

Andere talen zijn er juist om dingen makkelijker, duidelijker, efficiënter en compacter te maken.
Geen gebruik maken van de specifieke mogelijkheden van een bepaalde taal schiet het doel dus voorbij.
En juist bij een interpreter taal zoals Python is het erg zinvol om die extra functies te gebruiken, omdat die vaak veel beter en efficiënter zijn en vaak niet vertaalt hoeven te worden door de interpreter.

Hier een klein voorbeeld hoe je het onderhavige probleem zeer efficiënt zou kunnen oplossen en zelfs voor leken best te begrijpen is als je weet dat een list door blokhaken [] gedefinieerd wordt, dat een string dus een list is en dat een list comprehensions dus een nieuwe list en in dit geval een gefilterde list oplevert.

Voor de leken, hier staat gewoon:
Test voor elk karakter of hij in de standaard lijst van hoofdletters zit en zo ja voeg dat karakter dan toe aan de resulterende lijst en print de lengte van die zo gevonden lijst

Python:
1
2
3
4
5
6
import string

Regel = "Hello Everybody"

print len([Letter for Letter in Regel if Letter in string.ascii_uppercase]), 'Hoofd letters'
print len([Letter for Letter in Regel if Letter in string.ascii_lowercase]), 'Kleine letters'

[ Voor 8% gewijzigd door Ben(V) op 05-08-2019 14:19 ]

All truth passes through three stages: First it is ridiculed, second it is violently opposed and third it is accepted as being self-evident.


Acties:
  • +1 Henk 'm!

  • Wolfboy
  • Registratie: Januari 2001
  • Niet online

Wolfboy

ubi dubium ibi libertas

Als je performance in de praktijk afhangt van dit soort hacks dan ben je waarschijnlijk in de verkeerde taal bezig ja. Dit soort variaties zijn echt bedoeld als amusante exercitie. Op het moment dat je ze daadwerkelijk in productiecode gaat zetten dan ga je tegen de "Zen" van Python in.

Een goede Python programmeur gaat (naar mijn mening) niet voor kort, maar voor leesbaar:
Zen of Python
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
Dan heb je natuurlijk nogsteeds wel dingen die taal specifiek zijn, maar dat zou het juist leesbaarder moeten maken. Niet omgekeerd.

Om maar een voorbeeld te noemen, de for-else loop. Het heeft zeker zijn nut maar ik zou het niet in de praktijk gebruiken:
Python:
1
2
3
4
5
6
7
8
9
10
11
list_of_items = list(range(100))

search_query = 34

for item in list_of_items:
    if search_query == item:
        result = item
        break
else:
    print(f'Unable to find {search_query}')
    result = None

In dit specifieke voorbeeld is het wel duidelijk, maar ik zou het nogsteeds eerder zo aanpakken:
Python:
1
2
3
4
5
6
7
8
9
10
11
12
list_of_items = list(range(100))

search_query = 34

result = None
for item in list_of_items:
    if search_query == item:
        result = item
        break

if result is None:
    print(f'Unable to find {search_query}')

Blog [Stackoverflow] [LinkedIn]


Acties:
  • 0 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 12:01
Wolfboy schreef op maandag 5 augustus 2019 @ 15:42:
In dit specifieke voorbeeld is het wel duidelijk, maar ik zou het nogsteeds eerder zo aanpakken:
Ik vind het een goed voorbeeld, want ik moet zo'n for else toch vaak drie keer lezen. Ik snap het uiteindelijk wel, maar het is voor mij als meer generiek (imperatief) programmeur (met een focus op C) gewoon tegen intuïtief.

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • 0 Henk 'm!

  • Wolfboy
  • Registratie: Januari 2001
  • Niet online

Wolfboy

ubi dubium ibi libertas

CurlyMo schreef op maandag 5 augustus 2019 @ 15:46:
[...]

Ik vind het een goed voorbeeld, want ik moet zo'n for else toch vaak drie keer lezen. Ik snap het uiteindelijk wel, maar het is voor mij als meer generiek (imperatief) programmeur (met een focus op C) gewoon tegen intuïtief.
Exact ja :)

Zo zijn er nog ladingen andere voorbeelden te bedenken. Ik heb er een aantal in mijn boek benoemd (shameless plug: https://subscription.pack...-or-what-is-pythonic-code) maar over het algemeen komt het voornamelijk op gevoel neer denk ik.

List/dict/set comprehensions bijvoorbeeld kunnen best handig zijn voor kleine stukjes, maar zodra het niet meer op 1 regel past (80 of 120 tekens ofzo, wat je voorkeur is) kan je het meestal maar beter anders schrijven.

Een van de beste voorbeelden die ik zelf geschreven heb (wederom, voor het boek) was een quicksort door gebruik te maken van de Y-combinator. Een leuk stuk code maar verschrikkelijk onleesbaar tenzij je bijhoorlijk goed bekend met functioneel programmeren:

Python:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# De Y-combinator
>>> Y = lambda f: lambda *args: f(Y(f))(*args)

# Factorial met de Y-combinator:
>>> def factorial(combinator):
...     def _factorial(n):
...         if n:
...             return n * combinator(n - 1)
...         else:
...             return 1
...     return _factorial

>>> Y(factorial)(5)
120

# Quicksort via de Y-combinator
>>> quicksort = Y(lambda f:
...     lambda x: (
...         f([item for item in x if item < x[0]])
...         + [y for y in x if x[0] == y]
...         + f([item for item in x if item > x[0]])
...     ) if x else [])

>>> quicksort([1, 3, 5, 4, 1, 3, 2])
[1, 1, 2, 3, 3, 4, 5]

Blog [Stackoverflow] [LinkedIn]


Acties:
  • 0 Henk 'm!

  • ThomasG
  • Registratie: Juni 2006
  • Nu online
Zo'n for/else statement is gewoon contra-intuïtief voor niet Python programmeurs. Als je het de eerste keer ziet dan lijkt het logisch dat het wordt uitgevoerd als de opgegeven reeks leeg is. In plaats daarvan wordt het uitgevoerd als er tijdens het uitvoeren van de lus normaal verloopt, ofwel: geen break statement wordt aangeroepen 8)7

[ Voor 4% gewijzigd door ThomasG op 05-08-2019 18:37 ]


Acties:
  • 0 Henk 'm!

  • Ben(V)
  • Registratie: December 2013
  • Laatst online: 17:38
Wolfboy schreef op maandag 5 augustus 2019 @ 15:42:
Als je performance in de praktijk afhangt van dit soort hacks dan ben je waarschijnlijk in de verkeerde taal bezig ja. Dit soort variaties zijn echt bedoeld als amusante exercitie. Op het moment dat je ze daadwerkelijk in productiecode gaat zetten dan ga je tegen de "Zen" van Python in.
Tja iemand die zo iets basics en simpels als een list comprehension een hack noemt, maakt al weer veel duidelijk

En het voorbeeld dat je geeft met je for else maakt het ook duidelijk.
Geen enkel programmeur die zichzelf respecteert zou die constructie gebruiken.
De else code hoort gewoon net boven het break statement en niet in een else tak.

De for/else is in het algemeen een nutteloos en zou nooit toegepast mogen worden.

All truth passes through three stages: First it is ridiculed, second it is violently opposed and third it is accepted as being self-evident.


Acties:
  • +1 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 12:01
Ben(V) schreef op maandag 5 augustus 2019 @ 20:31:
[...]
De for/else is in het algemeen een nutteloos en zou nooit toegepast mogen worden.
Zijn we het daar over eens :)

Echter, waarom zou je dit doen:
Ben(V) schreef op maandag 5 augustus 2019 @ 13:50:
Python:
1
2
3
4
5
6
import string

Regel = "Hello Everybody"

print len([Letter for Letter in Regel if Letter in string.ascii_uppercase]), 'Hoofd letters'
print len([Letter for Letter in Regel if Letter in string.ascii_lowercase]), 'Kleine letters'
Als je ook gewoon dit kan doen, wat voor iedere dev gewoon beter leesbaar is.

Python:
1
2
3
4
5
6
7
8
9
10
11
12
13
import string

Regel = "Hello Everybody"

Uppercase = 0;
Lowercase = 0;

for Letter in Regel:
    Uppercase += (Letter in string.ascii_uppercase);
    Lowercase += (Letter in string.ascii_lowercase);

print(Uppercase + ' Hoofd letters');
print(Lowercase + ' Kleine letters');

Jouw constructie maakt helemaal geen gebruik van de optimalisaties die Guido in zijn artikel beschrijft, dus het performance nut is er niet. Het enige wat je hebt bereikt een stukje minder leesbare code.

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • 0 Henk 'm!

  • Wolfboy
  • Registratie: Januari 2001
  • Niet online

Wolfboy

ubi dubium ibi libertas

Ben(V) schreef op maandag 5 augustus 2019 @ 20:31:
[...]


Tja iemand die zo iets basics en simpels als een list comprehension een hack noemt, maakt al weer veel duidelijk
Je begrijpt me verkeerd. Ik noem diverse van de genoemde oplossingen in dit topic hacks omdat ze gewoon onleesbaar zijn zonder goede reden. Ik heb niets tegen list-comprehensions an sich en gebruik ze ook volop, maar ik plaats er wel de kanttekening bij dat het niet ten koste van de leesbaarheid moet gaan. Hoe subjectief dat ook is.

Blog [Stackoverflow] [LinkedIn]


Acties:
  • 0 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 12:01
Wolfboy schreef op dinsdag 6 augustus 2019 @ 01:04:
[...]
omdat ze gewoon onleesbaar zijn zonder goede reden.
Dat is precies het punt wat ik maak in mijn vorige reactie. Ik kan het artikel van Guido prima volgen. Dat je bepaalde specifieke Python trucs gebruikt om de interpreter te dwingen zoveel mogelijk in C zelf te laten doen. Dan moet je dat alleen wel doen, en niet alleen een for loop compacter maken omdat het nu eenmaal kan in Python.

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • +1 Henk 'm!

  • Ben(V)
  • Registratie: December 2013
  • Laatst online: 17:38
CurlyMo schreef op maandag 5 augustus 2019 @ 21:50:
Jouw constructie maakt helemaal geen gebruik van de optimalisaties die Guido in zijn artikel beschrijft, dus het performance nut is er niet. Het enige wat je hebt bereikt een stukje minder leesbare code.
Buiten het feit dat in Python een ; op het eind van het statement niet thuis hoort ondergraaf je zelf je eigen redenering.
Jij maakt met die ronde haakjes eerst een Boolean en daarna tel je die Boolean op bij een integer.
Volgens mij begrijpt niemand zonder hele diepe Python kennis iets van zo'n statement en kun je dat in geen enkele andere taal ook zo doen.

Verder heb ik het even getimed wat het verschil is tussen de list comprehension en jouw oplossing met onderstaande test.
En om te demonstreren wat het nut van het gebruikt van specifiek Python functies is heb ik daar ook maar even een timing van gemaakt.

Python:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import string, timeit, io
with io.open(file='D:\\test\\enwik8.txt',mode='r',encoding='utf-8') as fp:
    Data = fp.read()

start_time = timeit.default_timer()
Lowercase = len([Letter for Letter in Data if Letter in string.ascii_lowercase])
Duration1 = timeit.default_timer() - start_time

start_time = timeit.default_timer()
Lowercase = len([Letter for Letter in Data if Letter.islower()])
Duration2 = timeit.default_timer() - start_time

start_time = timeit.default_timer()
Lowercase = 0
for Letter in Data:
    Lowercase += (Letter in string.ascii_lowercase)
Duration3 = timeit.default_timer() - start_time
print Lowercase

with open('D:\\test\\result.txt', 'w') as fp:
    fp.write('Size of Data: ' + str(len(Data)) + ' bytes\n')
    fp.write('Duration 1  : ' + str(Duration1) + ' sec\n')
    fp.write('Duration 2  : ' + str(Duration2) + ' sec\n')
    fp.write('Duration 3  : ' + str(Duration3) + ' sec\n')

Dit is het resultaat:
Size of Data: 99621885 bytes
Duration 1 : 33.1437837432 sec
Duration 2 : 16.6936795289 sec
Duration 3 : 34.4892184689 sec
Volgens mij is mijn code veel leesbaarder en ook nog iets sneller al is het marginaal.
Ben het wel met je eens dat je een list comprehension niet te ingewikkeld moet maken, dan schiet het z'n doel voorbij.
Het is bedoelt om een kleine, snelle en overzichtelijke actie op een list te doen.

[ Voor 17% gewijzigd door Ben(V) op 06-08-2019 12:47 ]

All truth passes through three stages: First it is ridiculed, second it is violently opposed and third it is accepted as being self-evident.


Acties:
  • +1 Henk 'm!

  • CurlyMo
  • Registratie: Februari 2011
  • Laatst online: 12:01
Ben(V) schreef op dinsdag 6 augustus 2019 @ 12:27:
[...]
Buiten het feit dat in Python een ; op het eind van het statement niet thuis hoort ondergraaf je zelf je eigen redenering.
Zullen we die discussie niet aangaan ;)
Jij maakt met die ronde haakjes eerst een Boolean en daarna tel je die Boolean op bij een integer.
Volgens mij begrijpt niemand zonder hele diepe Python kennis iets van zo'n statement en kun je dat in geen enkele andere taal ook zo doen.
Het was illustratief :) Een cast naar een int was netter geweest. Overigens denk ik dat het in elke weak typed talen wel zou werken.
Volgens mij is mijn code veel leesbaarder en ook nog iets sneller al is het marginaal.
Ben het wel met je eens dat je een list comprehension niet te ingewikkeld moet maken, dan schiet het z'n doel voorbij.
Het is bedoelt om een kleine, snelle en overzichtelijke actie op een list te doen.
Mijn mening is dat je list comprehension alleen moet toepassen als het een daadwerkelijk functioneel doel dient. Leesbaarheid is daar wat mij betreft geen onderdeel van, omdat het daarvoor te Python specifiek is. Je maakt het daarvoor voor het gros van de programmeurs minder leesbaarder van. Performance is nauwelijks een factor in deze, dus dat functionele doel is er al niet.

Maar aangezien we op het punt van smaak zijn aanbeland heeft verder discussiëren geen zin meer. We begrijpen elkaar, en we zijn het eens dat we het op dat punt oneens zijn ;)

[ Voor 6% gewijzigd door CurlyMo op 06-08-2019 13:12 ]

Sinds de 2 dagen regel reageer ik hier niet meer


Acties:
  • 0 Henk 'm!

  • Wolfboy
  • Registratie: Januari 2001
  • Niet online

Wolfboy

ubi dubium ibi libertas

List/set/dict comprehensions hebben vooral doel op het moment dat je het resultaat op moet slaan, dan is het gewoon een stuk korter en eenvoudiger. Bijvoorbeeld:

Python:
1
2
3
original_list = list(range(1000))

odd_hex_list = [hex(x) for x in original_list if x % 0]

Versus:

Python:
1
2
3
4
5
6
original_list = list(range(100))

odd_hex_list = []
for x in original_list:
    if x % 0:
        odd_hex_list.append(hex(x))


Als het dus alleen zoiets is... doe het dan niet:

Python:
1
[some_function(x) for x in some_list]

[ Voor 11% gewijzigd door Wolfboy op 06-08-2019 16:50 ]

Blog [Stackoverflow] [LinkedIn]

Pagina: 1