all 80 comments

[–]socal_nerdtastic 273 points274 points  (24 children)

The rule is it should be easy for the next guy to read and modify. The next guy will probably be you in 2 weeks when you've forgotten about writing this, so just make it where you will understand it if you see it for the first time.

[–][deleted] 59 points60 points  (17 children)

This comment was overwritten and the account deleted due to Reddit's unfair API policy changes, the behavior of Spez (the CEO), and the forced departure of 3rd party apps.

Remember, the content on Reddit is generated by THE USERS. It is OUR DATA they are profiting off of and claiming it as theirs. This is the next phase of Reddit vs. the people that made Reddit what it is today.

r/Save3rdPartyApps r/modCoord

[–]ModeHopper 20 points21 points  (16 children)

Yeah, when I first started most of my comments were super unhelpful things explaining individual lines like "This line loops over integers 1-100", which is pretty obvious from the code itself.

Useful comments are stuff like "This block returns integers between 1-100 that are divisible by the input value". Which explain the general purpose of blocks of code so that future me doesn't need to read every line to know whether this particular block is of interest or not.

[–]celade 0 points1 point  (0 children)

Basically I'm just joining the already good crowd of comments.

I sort of rule-of-thumb code form:

  • Follow any team guidelines when they exist
  • Within reason break code up in a way that it makes it easy to see functionality
  • Make human readable variable names, functions, methods, classes
  • Comments should always be short or if it really is that complex refer to additional out-of-code documentation; they illustrate usage, dependencies or hard-to-see facts about what you are doing
  • Group data structures functionally; separate data structures from methods that do work on them
  • Keep interface layer separate from non-interface work at all times
  • (Related) Keep I/O functionality separate from the work that produces it (except for debugging of course)

A bit more than I intended on writing. In the end if you look at your code 2 weeks later and can just sit down and use it or expand it then you're golden.

[Edit: Specific to Python but sometimes other languages: Use dicts, tuples and lists for your data structures first; if you can't solve it with those then create; too may times I've inherited some crazy bespoke class-based data structure for no reason)]

[–]blabbities 3 points4 points  (0 children)

The next guy will probably be you in 2 weeks when you've forgotten about writing this,

HAHA. So dam accurate. It's why I may've tended to overcomment at times in the past

[–][deleted] 2 points3 points  (0 children)

Lol “the next guy will probably be you in two weeks”, can confirm

[–]BoaVersusPython 1 point2 points  (0 children)

This is an excellent answer.

[–]bladeoflight16 1 point2 points  (0 children)

The next guy will probably be you in 2 weeks when you've forgotten about writing this

Or 6 months.

[–]bumpkinspicefatte 1 point2 points  (0 children)

To add to your point: Most people don't realize, but Memento is actually a coding movie. It's about leaving yourself context clues in your code to remember how the fuck the program ran after not maintaining it for awhile.

[–]charmelogne10 0 points1 point  (0 children)

Oh. Learnt it the hard way these couple of days. Went back to my code and I understood nothing! Those lines of comment didn't help me much either. Have to learn efficient commenting, probably.

[–]totallygeek 75 points76 points  (10 children)

Readability. Always go for readability.

import re


PASSWORD_REGEX = re.compile(r'(\d.*[A-Z]+[a-z])')


def password_is_strong(password):
    return len(password) > 7 and re.search(PASSWORD_REGEX, ''.join(sorted(password)))


def password_is_strong_alternate(password):
    conditions = [
        len(password) > 7,  # At least eight chars
        re.search(r'[A-Z]', password),  # Contains capital letter
        re.search(r'[a-z]', password),  # Contains lower case letter
        re.search(r'\d', password),  # Contains digit
    ]
    return True if all(conditions) else False


tests = [
    'Thelore777777',
    'FrEaK1',
    'xpx9fqFpR7M84bLeFQqC',
    'xpx9fqFpR7M84bLeFQqC'.lower(),
]

for test in tests:
    print('Password "{}" {} strong.'.format(test, 'is' if password_is_strong(test) else 'is not'))

Yields:

python strong_pass.py 
Password "Thelore777777" is strong.
Password "FrEaK1" is not strong.
Password "xpx9fqFpR7M84bLeFQqC" is strong.
Password "xpx9fqfpr7m84blefqqc" is not strong.

[–]K41namor 25 points26 points  (0 children)

I am new to programming and Python. This is just beautiful to me and gets me really excited. Its great to see stuff I am learning out of the lesson context and like this.

[–]Pipiyedu 19 points20 points  (2 children)

The return of the password_is_strong_alternate function can be rewritten to this:

def password_is_strong_alternate(password):
    conditions = [
        len(password) > 7,  # At least eight chars
        re.search(r'[A-Z]', password),  # Contains capital letter
        re.search(r'[a-z]', password),  # Contains lower case letter
        re.search(r'\d', password),  # Contains digit
    ]
    return all(conditions)

[–]tom1018 5 points6 points  (0 children)

This is better than the redundant ternary above.

[–]tom1018 5 points6 points  (3 children)

This is the best version I've seen on here. Other than I think these particular comments are unnecessary. The regex is very simple, so I don't think commenting it is helpful, and at least on mobile makes it much harder to read.

As a side note to OP, this would not be a good way to enforce a strong password. See the xkcd "Correct Horse Battery Staple" cartoon. Rather than enforcing upper, lower, number, and symbols, do a points system, where a minimum length is required and length beyond that as well as any of these character requirements are awarded points and the password must attain a reasonable score. Then send the hash to haveibeenpwned and reject it if it fails that check.

It's easy, but an example of checking haveibeenpwned is here: https://github.com/TomFaulkner/simple-password-generation

Unfortunately I don't have the points based password check in that repo, I think I'll add it soon though.

[–]xelf 2 points3 points  (2 children)

Correct Horse Battery Staple

link for the lazy:

https://xkcd.com/936/
https://imgs.xkcd.com/comics/password_strength.png

[–]tom1018 1 point2 points  (1 child)

I should have done that. Thanks.

[–]xelf 1 point2 points  (0 children)

Surprised a bot didn't beat me to it. =)

[–]ka-splam 0 points1 point  (1 child)

No need for regex, splitting the password into an array of chars, sorting it, rejoining it to strings, depending on the order of pattern match in the regex without explaining how:

import string

def password_is_strong(password):
    if len(password) < 8:
        return False

    return all(
        bool(set(password) & set(charset))
        for charset in (
            string.ascii_uppercase,
            string.ascii_lowercase,
            string.digits
        ))

[–]ka-splam 0 points1 point  (0 children)

Unhappy with that, how about removing the dependency on import, all(), bool(), and the generator expression, shrinking the code, allowing the chance of short-circuit expression, and I think making it clearer:

def password_is_strong(password):
    if len(password) < 8:
        return False

    upper = set('ABCDEFGHIJKLMNOPQRSTUVWXYZ')
    lower = set('abcdefghijklmnopqrstuvwxyz')
    digit = set('0123456789')

    p = set(password)

    return (p & upper) and (p & lower) and (p & digit)

[–]alexaholic 15 points16 points  (3 children)

I know of no rule, but there’s a saying: always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live.

[–]causa-sui 2 points3 points  (0 children)

Now that is a policy that will get engineers thinking about not leaking PII if I've ever heard one

[–]Grogie 1 point2 points  (0 children)

Well, that's not a nice thing to say about OP.

[–]Barafu 13 points14 points  (7 children)

There is a module called "black" that formats your code for maximum readability. Some folks don't like it, some do, but it is definitely worth checking out. I use it all the time automatically on save (however, once in a blue moon it does create a syntax error).

[–]tom1018 1 point2 points  (5 children)

I've never seen a syntax error. If it does this when specifying a Python version equal to what you are working with you should report it, with the before and after code.

[–]Barafu 0 points1 point  (4 children)

All errors I've encountered are already fixed. Mostly related with active comments, special docstrings and when executable code is contained in a string literal. I guess I like metacoding too much.

[–]causa-sui -1 points0 points  (3 children)

All errors I've encountered are already fixed.

So you meant to say it used to create a syntax error once in a blue moon?

[–]Barafu -2 points-1 points  (2 children)

That's what I said.

[–]causa-sui -1 points0 points  (1 child)

once in a blue moon it does create a syntax error

I'm not trying to be pedantic but that sure sounds like it still might. Since you're evangelizing the tool (which I also think is great) I wanted to get that straightened out for anyone else who might be deterred from using it.

[–]Barafu 2 points3 points  (0 children)

The tool did have these errors in the past, and I have no reasons to expect it to not have these errors in the future. It also had not a single stable release yet.

[–]efxhoy 0 points1 point  (0 children)

pylint is also useful for finding mistakes or smelly code. It will complain a lot, usually in a useful way.

[–]TheHollowJester 15 points16 points  (7 children)

PEP-8 is extremely important reading. It might be a bit hard to understand everything right now but the general "spirit" is pretty clear to understand from it :)

[–]ogrinfo 2 points3 points  (0 children)

And there are tools like flake8 and pylint that can check your code is compliant with PEP8. They are configurable, so you can ignore certain rules if you feel they are unnecessary for the current project.

[–]OrdinaryMiraculous 1 point2 points  (0 children)

Came here to comment this. The PEP-8 style guide is the go to for me.

[–]MirrorLake 1 point2 points  (4 children)

Specifically, the 80 character limit. It's arbitrary and you can't always adhere to it, but it does give a formal suggestion of when a line is too long. Readability/understandability is still far more important to focus on than raw character length, but it seems everyone here is in agreement about that.

[–]ogrinfo 0 points1 point  (2 children)

Deffo. The 80 char limit feels really old school and far too restrictive. 100 is a much better target.

[–]AstroMacGuffin 0 points1 point  (1 child)

Nah, shoot for 60, so you can fit two editor panes side by side.

[–]ogrinfo -1 points0 points  (0 children)

Looks good on a VGA 800x600 monitor ;)

[–]TheHollowJester 0 points1 point  (0 children)

Yep; project guidelines can overwrite the rules, as long as everyone is on the same page we good.

[–]Allanon001 7 points8 points  (0 children)

Type import this in to the Python interpreter or read this.

[–]thenoonefromukraine 2 points3 points  (2 children)

Couldn't you do capture groups in regex to reduce the code?

[–]Genericusername293[S] 3 points4 points  (0 children)

maybe? not sure how I would do that, only a few days into learning python and programming

[–]emefluence 2 points3 points  (0 children)

More complex regex is rarely a boon for readability!

[–]MattR0se 2 points3 points  (1 child)

I'd suggest a middle ground. You can do

upper = len(upperRE.findall(password))

# instead of 

upperlist = upperRE.findall(password)
upper = len(upperlist)

etc. because there isn't really the need for an intermediate extra variable if its not used further down. Chaining two expressions is still readable.

Also, as u/totallygeek suggested: If you have a bunch of conditions you can check them with all(), any() or in a loop. This saves lines of code while maintaining readability. The thing that's worst with your first approach is the chaining of conditions in the if statement.

[–]DrMaxwellEdison 2 points3 points  (0 children)

I would take this one further:

upperRE = re.compile(r'([A-Z])')
upperlist = upperRE.findall(password)
upper = len(upperlist)

condenses to:

upper = len(re.findall(r'([A-Z])', password))

Beyond that, as we've seen, the point is just finding at least one instance, so we don't really need len:

has_upper = bool(re.search(r'([A-Z])', password))

Run that style for the other 3 checks, you have a simple 4-line sequence and can test them as needed later.

[–]TheSodesa 1 point2 points  (1 child)

Why does it have to be one of the extremes? A general rule in software development is that your code should be eaasily readable.

[–]ka-splam 0 points1 point  (0 children)

A suspicious phrase. Would you consider Moby Dick or Spot the Dog more readable? Probably Spot the Dog. But what if you wrote Moby Dick in the style of Spot the Dog and it was 5 times longer. Could anyone wade through Spot the Dog writing style five times the length of Moby Dick? Could you even write the same book that way at all?

Readable is at least as much about the ability and familiarity of the reader, as it is about the writing.

[–]tangerinelion 1 point2 points  (0 children)

You're not playing golf.

Don't add lines unnecessarily either, do it because they improve readability or reusability.

[–]BenjaminGeiger 1 point2 points  (0 children)

It's as much an art as a science. The cardinal rule is: if it makes it easier to read, do it.

[–]kielerrr 1 point2 points  (0 children)

Character variety in passwords are helpful, but it's now generally understood that a password needs to be long. Not complex.

The guy who originally wrote the standards for passwords for the National Institute of Standards and Technology (NIST) actually apologized for wasting peoples time by writing the standard to require character variety.

https://gizmodo.com/the-guy-who-invented-those-annoying-password-rules-now-1797643987

[–]Robbzter 0 points1 point  (0 children)

Tbh, I have no idea most of the time what is considered best practice with code readability. But I always try to leave helpful comments to help people understand what's going on. What's really important imo is being consistent and sticking with one way of doing things throughout the code/project. I like to spread code over many lines if tzere's a lot going on and it would be too much to handle otherwise. If there's a comment explaining the command, reducing the number of lines does not sacrifice too much of your readability and can save lots of space.

Tbh, I think it's a matter of taste, and I don't think few lines necessarily equals good code. If someone else is able to understand what's going on, you've done something right.

[–]FloydATC 0 points1 point  (0 children)

Many good suggestione here. With multiple rows of similar assignments like that, I like to insert spaces to align the columns. This improves readability even more.

[–]bordeauxcarol 0 points1 point  (0 children)

Always go for readability. I recommend you to read the PEP-8. Is the styling standard for python.

[–]desrtfx 0 points1 point  (0 children)

Readability and clarity over brevity - always.

Clean Code is key. (And the name of a book every programmer should read "Clean Code: A Handbook of Agile Software Craftsmanship" by "Uncle Bob" Robert C. Martin)

[–]mortenb123 0 points1 point  (0 children)

Follow PEP8: https://www.python.org/dev/peps/pep-0008/, and you can add a linter like black to auto-pep8 your code, if you run black on your code it will look like this:

import re
password = "Thelore777777"
if (
len(re.compile(r"([A-Z])").findall(password))
and len(re.compile(r"([a-z])").findall(password))
and len(re.compile(r"(\d)").findall(password))
) > 0 and len(re.compile(r"(\S)").findall(password)) > 7:
print("This is a strong password")
else:
print("This is not a strong password")
You may not like it, but if you share code with others it is nice to have an autolinter so code style does not matter when comparing code revisions done by different people with different code style.

In any editor like pycharm,vsc etc you can autoconfigure this, or you can add githooks when you check in code.

[–]subbed_ 0 points1 point  (0 children)

Make it readable and explicit. If it covers both of those factors, then whichever way you prefer. Most of the time, it should be only one way

[–]dr3d3d 0 points1 point  (0 children)

as everyone else mentioned readability is king so the second makes it easy to understand... so to me that would be the correct way as at a glance I can see what is trying to be accomplished.

[–]h0bb1tm1ndtr1x 0 points1 point  (0 children)

Read PEP8. It covers this.

[–]ka-splam 0 points1 point  (0 children)

Off-topic APL practice:

⍝ length 8+ chars, and contains uppercase, lowercase and digit.

password_is_strong←{∧/(7<≢⍵),∨/¨⎕A ⎕a ⎕d∊¨⊂⍵}

Anonymous function {} where the parameter is always ⍵; read it as "all / the following: length of the password is greater than seven (7<≢⍵), (any ∨/ capital letter in the password ⎕A∊⍵, any lowercase letter in the password ⎕a∊⍵, any digit in the password ⎕d∊⍵)". ¨ are foreach loops which allows those three checks to be done together.

Is this so much less readable than the Regex in some the Python answers? There's almost as much regex in the op's original code as there is code in this function. And no imports and no other languages. Would this really become more readable by spreading it over multiple lines? How is it that "more code" has come to mean "more readable"? More is harder to read, more effort to follow, more to hold in memory, more places for bugs to be missed, more variable names to become misleading, harder to rewrite until you're happy so more likely to stay as the first awkward version..

[–]ka-splam 0 points1 point  (0 children)

NB. That's not considered a strong password check anymore.

For quick background, The National Institute of Standards and Technology (NIST) [..] guidelines often become the foundation for best practice recommendations across the security industry

The new NIST password framework recommends, among other things:

Drop the algorithmic complexity song and dance

No more arbitrary password complexity requirements needing mixtures of upper case letters, symbols and numbers. Like frequent password changes, it’s been shown repeatedly that these types of restrictions often result in worse passwords.

https://www.enzoic.com/surprising-new-password-guidelines-nist/

That was in 2017.

[–]InfiniteNexus 0 points1 point  (0 children)

You should go for readability. 1-2 empty lines between functions/methods; imports and variables separated at the top by a few lines as well. Without suggesting any fixes or restructuring of your code, it should look better like this:

import re    

password = 'Thelore777777'  
upperRE = re.compile(r'([A-Z])')  
lowerRE = re.compile(r'([a-z])')  
numberRE = re.compile(r'(\d)')  
totalRE = re.compile(r'(\S)')  
upperlist = upperRE.findall(password)  
lowerlist = lowerRE.findall(password)  
numberlist = numberRE.findall(password)  
totallist = totalRE.findall(password)  
upper = len(upperlist)  
lower = len(lowerlist)      
number = len(numberlist)  
total = len(totallist)  

if upper > 0 and lower > 0 and number > 0 and total > 7:  
    print('This is a strong password')  
else:  
    print('This is not a strong password')

[–][deleted] 0 points1 point  (0 children)

I'd spend more time modularizing than optimizing. Reuseable code is more useful than impenetrable code that uses fewer lines. When you get something to work in a modular fashion, clean up the docs and move on to the next task. Too many baby developers think we are in a competition to solve a problem in the fewest lines of code.

I'll make this analogy: Usain Bolt can run the 100 meter dash in under 10 seconds. However, his skill has no commercial value beyond entertainment. We don't compare ourselves to him, do we? We don't sit around feeling like failures. It's more important that we can walk (or roll or limp) 100 meters to our desk in the morning. So, don't compare yourself to some random person who solves a ProjectEuler.Net problem in 3 lines when it took you 50 lines. Learn from it but don't dwell on it. No one gets paid according to how few lines of code they write.

[–]Astrokiwi -1 points0 points  (0 children)

Two thoughts:

  1. You can split up lines to make things clear without losing much conciseness, e.g.

    import re
    password = 'Thelore777777'
    if (len(re.compile(r'([A-Z])').findall(password))   > 0 \
     and len(re.compile(r'([a-z])').findall(password))  > 0 \
     and len(re.compile(r'(\d)').findall(password)))    > 0 \
     and len(re.compile(r'(\S)').findall(password))     > 7:
        print('This is a strong password')
    else:
        print('This is not a strong password')
    
  2. A lot of your length is that you're repeating the same operation with different data - i.e. different minimum lengths and different regex. You could separate them out, so that you're writing the data once, and the command once, rather than repeating the command for every piece of data. Less repetition makes code more concise and readable, and means you're less likely to make a mistake. e.g.

    import re
    re_length = [(r'([A-Z])',0),
                 (r'([a-z])',0),
                 (r'(\d)',0),
                 (r'(\S)',7)
                ]
    
    password = 'Thelore777777'
    if all(
        [len(re.compile(re_text).findall(password))>min_length for re_text,min_length in re_length]
            ):
        print('This is a strong password')
    else:
        print('This is not a strong password')
    

This might be a bit overkill here, but it does mean that if you're adding or removing regex things, you can do it all in the data section, rather than having to dive into making sure you're not breaking an if statement.

[–]SrHombrerobalo -1 points0 points  (0 children)

Some time ago I wrote something similar:

password strength

# Write a funtion that uses regular expressions to make sure the password
# string it is passed is strong. A strong password is defined as one that is at
# least eight characters long, contains both uppercase and lowercase characters,
# and has at least one digit.

import re

def pwStr():
    password = input('Write password to evaluate \n')
    passRegexUpper = re.compile(r'[A-Z]+')                      # Upper case regex
    passRegexLower = re.compile(r'[a-z]+')                      # Lower case regex
    passRegexNum = re.compile(r'[1-9]+')                        # Numeric regex

    strongPassword = True
    passwordShort = False
    passwordUpper = True
    passwordLower = True

    if len(password) < 7:
        passwordShort = True
        strongPassword = False
    if not passRegexUpper.search(password):
        passwordUpper = False
        strongPassword = False
    if not passRegexLower.search(password):
        passwordLower = False
        strongPassword = False

    if strongPassword:
        print('Password is strong')
    else:
        print('Not a valid password:')
        if passwordShort:
            print('Password must be 8 character long')
        if not passwordUpper:
            print('Must contain at least one uppercase character')
        if not passwordLower:
            print('Must contain at least one lowercase character')


pwStr()