Ann
Ann

Reputation: 11

Random password generation with digit, letter, symbol

I want to generate a random password.

It should be made up of symbol, letter, and digit such as:

tqpV4aJ

I!WZuYvBv7

S@OPToyu0u

a) Contains 6-10 characters

b) Contains exactly 1 symbol from the set {!,@,#,-,@,+,%}.

c) Contains exactly 1 digit, i.e., any of the numbers 0-9 except for the number 5, which is excluded.

d) Contains upper or lower case letters among the remaining characters, except for ’e’ and ’E’, which are excluded

e) The positions of the characters are chosen randomly.

How can I achieve this in a pythonic way?


my attempt (fail: can only output one letter):

import random
import string

symbol=('!','@','#','-','@','+','%')
x= random.choice(symbol)

digit= random.randint(0,9)
if digit!=5:
    y= digit

letter= random.choice(string.ascii_letters)
if letter!= 'e' or 'E':
    z= letter

def main():    
  list= x + str(y) + z
  size= random.randint(6, 10)
  pw=''.join(random.choice(list) for i in range(size))
  print pw

main()

Upvotes: 0

Views: 1675

Answers (5)

user3064538
user3064538

Reputation:

Step 1: upgrade to Python 3, step 2:

import secrets
import string

letter_choices = string.ascii_letters.replace("e", "").replace("E", "")
num_letters = secrets.choice(range(4, 9))  # between 4 and 8 letters
password = [secrets.choice(letter_choices) for _ in range(num_letters)]

symbol = secrets.choice("!@#-+%")
password.insert(secrets.randbelow(len(password) + 1), symbol)

digit = secrets.choice("012346789")  # 5 is excluded
password.insert(secrets.randbelow(len(password) + 1), digit)

print(''.join(password))

Upvotes: 0

Peter O.
Peter O.

Reputation: 32878

All the answers written here should use the secrets module rather than the random module to generate secret values, including passwords. In particular:

  • random.randint(0, n) should be replaced with secrets.randbelow(n+1).
  • random.choice(x) should be replaced with secrets.choice(x).
  • random.choices(...) should be replaced with secrets.SystemRandom().choices(...) or random.SystemRandom().choices(...).
  • random.shuffle(...) should be replaced with secrets.SystemRandom().shuffle(...) or random.SystemRandom().shuffle(...).

The functions in the random module (including random.randint, random.choice, random.choices, random.sample, and random.shuffle) use a global random number generator that's not necessarily designed for information security, so using those functions can make passwords easier to guess than they already are -- especially in view of the maximum length requirement and restricted character distribution you give in your question. In general, whenever random.* functions are called to generate passwords, they should be replaced by secrets.SystemRandom().* instead (or a suitable method in the secrets module).

Upvotes: 2

Bobby Ocean
Bobby Ocean

Reputation: 3328

It sounds like you want:

import random, string

symbols = '!@#-@+%'   #choose 1
digits  = '123467890' #choose 1
letters = set(string.ascii_letters)-set('eE') #choose 4 to 8

chars   = random.sample(letters,random.randint(4,8))
chars.append(random.choice(symbols))
chars.append(random.choice(digits))
random.shuffle(chars)

pwd = ''.join(chars)

Upvotes: 0

TechPerson
TechPerson

Reputation: 340

I believe your approach can be simplified with more listcomps and careful character replacing:

import random
import string

def get_password():
    pass_ = [random.choice(list(set(string.ascii_letters) - {"e", "E"})) for i in range(random.randint(6, 10))]  # Initial password contains random letters, excluding e and E
    index_1 = random.randint(0, len(pass_) - 1)
    pass_[index_1] = random.choice(["!", "@", "#", "-", "@", "+", "%"])  # Replace a random character with a symbol
    index_2 = index_1  # Initialize index_2 as equal to index_1 so that it will be randomized at least once

    while index_1 == index_2:  # Ensure the two indexes are not equal
        index_2 = random.randint(0, len(pass_) - 1)

    pass_[index_2] = str(random.choice([random.randint(0, 4), random.randint(6, 9)]))  # Replace the chosen random character with a random digit, excluding 5

    return "".join(pass_)

print(get_password())

Upvotes: 0

Charles Angus
Charles Angus

Reputation: 424

Should be fairly clear from the comments. Basic idea is:

  • build up lists to choose from
  • make choices
  • join into a single list
  • shuffle the list
  • join back to a single string

It should be noted this is not secure, and should not be used to actually generate passwords.

import random
import string

# it's nice to have your constants up top and in CAPS
SYMBOLS = ('!', '@', '#', '-', '@', '+', '%')
# it's easy to just omit '5' from a list of numbers
# easier to just have the list already be strings than mess around with ints
NUMBERS = ('0', '1', '2', '3', '4', '6', '7', '8', '9')
# quick list comprehension to omit 'e' and 'E'
LETTERS = [char for char in string.ascii_letters if char != 'e' and char != 'E']

# put all our logic in a function
def create_password_insecure():
    # pick a symbol
    chosen_symbol = random.choice(SYMBOLS)
    # pick a number
    chosen_number = random.choice(NUMBERS)
    # pick some letters to fill out our 6-10 chars
    # we pick 4-8 not 6-10, since 2 chars will be symbol/number
    number_of_letters = random.randint(4, 8)
    # quick list comprehension to pick our letters
    chosen_letters = [random.choice(LETTERS) for i in range(number_of_letters)]
    # get all our elements in a single list
    password_elements_list = [chosen_symbol] + [chosen_number] + chosen_letters
    # (pseudo)randomize the list
    random.shuffle(password_elements_list)

    pseudorandom_password = ''.join(password_elements_list)
    print (pseudorandom_password)

create_password_insecure()

Upvotes: 0

Related Questions