2019-02-06 22:35:35 8 Comments
I am writing a program to count the number of uppercase and lowercase letters in a string. I came up with something that works, but as I am still a beginner I have a feeling writing the code this way is probably considered "clumsy."
Here is what I have:
stri = input("Give me a phrase:")
stri_up = 0
stri_lo = 0
for i in stri:
if i.isupper():
stri_up += 1
if i.islower():
stri_lo += 1
print("The number of uppercase letters in your phrase is:", stri_up)
print("The number of lowercase letters in your phrase is:", stri_lo)
Output:
Give me a phrase: tHe Sun is sHininG
The number of uppercase letters in your phrase is: 4
The number of lowercase letters in your phrase is: 11
I would like to learn how to write neat, beautiful code so I am wondering if there is a more efficient and elegant way to code this.
Related Questions
Sponsored Content
2 Answered Questions
[SOLVED] String validation based on multiple criteria
- 2019-07-09 10:39:34
- mishsx
- 50 View
- 4 Score
- 2 Answer
- Tags: python python-3.x strings
2 Answered Questions
[SOLVED] Guess-the-number game by a Python beginner
- 2019-01-26 23:22:23
- Dewayne Redding
- 242 View
- 4 Score
- 2 Answer
- Tags: python beginner python-2.x number-guessing-game
2 Answered Questions
[SOLVED] Make a beautiful binary string
- 2018-10-01 14:19:48
- db18
- 3068 View
- 12 Score
- 2 Answer
- Tags: python strings programming-challenge
1 Answered Questions
2 Answered Questions
[SOLVED] Transform String a into b
- 2017-11-22 09:00:52
- kumarmo2
- 229 View
- 2 Score
- 2 Answer
- Tags: java algorithm strings memoization
3 Answered Questions
[SOLVED] Use Python to determine the repeating pattern in a string
- 2017-04-20 23:10:29
- Jake Steele
- 8391 View
- 3 Score
- 3 Answer
- Tags: python performance beginner algorithm python-2.x
2 Answered Questions
[SOLVED] Python traverse a directory recursively and print contact numbers
- 2017-01-22 09:02:38
- Sinscary
- 2177 View
- 8 Score
- 2 Answer
- Tags: python beginner python-2.x recursion regex
1 Answered Questions
[SOLVED] Haskell CodeEval Beautiful Strings
- 2015-12-16 19:11:19
- BlindGarret
- 247 View
- 5 Score
- 1 Answer
- Tags: beginner programming-challenge haskell
2 Answered Questions
[SOLVED] These words are so dirty
- 2015-07-19 08:39:25
- Legato
- 740 View
- 8 Score
- 2 Answer
- Tags: java strings programming-challenge regex
4 Answered Questions
[SOLVED] Improving efficiency for finding longest contiguous non-decreasing substring
- 2013-10-26 13:14:37
- Akshat Tripathi
- 7389 View
- 4 Score
- 4 Answer
- Tags: python optimization algorithm strings
4 comments
@Matt 2019-02-07 09:59:05
TLDR: Looks good! This is perfectly reasonable solution for your problem. It's certainly not clumsy.
Optimisations The optimisation ShadowRanger points out, is faster, due to compiler optimisations, I wouldn't worry about this at a beginner level (and not even at an experienced level really, unless it was critical to make every optimisation).
The optimisation of checking only
isupper
orislower
that some have pointed out probably isn't valid. If your input is guaranteed to be only alphabetic characters A-Z or a-z, then you can assume that if it's not upper, it's lower. But this doesn't apply generally. '1' is neither lower nor upper for example. Checking onlyisupper
and assuming the opposite on aFalse
result, you would increment your 'lower' counter and that wouldn't be correct.Your code provides a correct solution and doesn't break when the user inputs an empty string or non alphabetic characters, which is why I'd consider it good.
Possible next step: Since you say you're a beginner, I'd look up writing tests if you haven't already and learn a little about how to write good tests. Checking empty input and special characters would be an interesting start. Some terms to search would be edge-case
@Madrid_datalady 2019-02-07 11:11:22
Thank you, your comment warmed my heart and has very useful suggestions. :)
@user192377 2019-02-07 17:14:30
You can approach this in a cleaner manner by using the filter function; for example:
As a note this is a less efficient approach, since you iterate through the string twice in the filter calls, but it is a different way of approaching the problem, and hopefully get you introduced to some more advanced python techniques.
@JAD 2019-02-07 09:35:38
Small optimisation
If you know a character is an upper, you don't have to test for lower anymore:
@Baldrickk 2019-02-08 09:25:14
what about punctuation?
@ShadowRanger 2019-02-07 00:09:11
Your code is mostly fine. I'd suggest more meaningful names for variables, e.g.
i
is typically a name for integer/index variables; since you're iterating over letters/characters, you might choosec
,char
,let
, orletter
. Forstri
, you might just name itphrase
(that's what you asked for from the user after all). You get the idea. Make the names self-documenting.Arguably you could make it look "prettier" by performing a single pass per test, replacing:
with:
That's in theory less efficient, since it has to traverse
stri
twice, while your original code only does it once, but in practice it's likely faster; on the CPython reference interpreter,sum
is highly optimized for this case and avoids constructing a bunch of intermediateint
objects while summing.@200_success 2019-02-07 01:48:27
You can just do
sum(c.isupper() for c in phrase)
, because boolean will be treated as 0 or 1 when summing.@ShadowRanger 2019-02-07 02:47:49
@200_success: True, but I'm using dirty knowledge here; the
sum
fast path only fires forint
(PyLong_Object
at C layer) exactly (noint
subclasses accepted, includingbool
); yieldingbool
blocks that optimization (and involves a lot more yields from the genexpr that can be avoided). Plus, I consider it more obvious to actually sum integers conditionally; usingbool
for numeric value is perfectly legal, just a little more magical than necessary, given the minimal benefit.@ShadowRanger 2019-02-07 03:20:39
Just for comparison, a microbenchmark where
stri
/phrase
is just one of each ASCII character (''.join(map(chr, range(128)))
), takes 15.3 µs to complete on my computer using your code, vs. 10.5 µs for summing hardcoded1
s conditionally.@Baldrickk 2019-02-07 16:21:48
Your theory vs practice may be a little off - for short strings, it likely matters little anyway, but putting a long string through the function may very well cause it to invalidate the cache (you just know someone's going to try passing it the entire works of shakespeare all at once). This would make the cache friendly single pass much more efficient where it really counts. Probably... I really should profile this.
@Baldrickk 2019-02-07 16:29:21
@200_success
"But", I thought, "wouldn't a lot of punctiation (e.g. ./@#~";:' etc.) cause that single line to be incorrect?"
= 2 uppers and 109 lowers when it should be 70 lowers.@ShadowRanger 2019-02-07 22:14:36
@Baldrickk: I modified the microbenchmark to run against the contents of Ubuntu's
american-english-insane
file repeated 10 times (len
of 68753140). Mysum
was fastest by a small amount (for 10x case, 8.34 s), the OP's code close behind (8.48 s), and the 200_success's rather further behind (11 s). The same pattern held for unrepeatedamerican-english-insane
, with the same margins. I suspect the cache doesn't matter; any system worth its salt can recognize sequential memory access and populate the cache ahead of time (Python is slow enough to give it time to do so).@ShadowRanger 2019-02-07 22:15:51
Regardless, I was suggesting it mostly as cleaner looking code (it's shorter, and each line does exactly one obvious thing, no need for context to understand it); the mild speed boost doesn't really matter.
@Baldrickk 2019-02-08 09:24:37
@ShadowRanger thanks! Always good to know. I'm used to working with programs where that is a big thing.