Password Generator Python I decided to make a password generator to get more familiar with Python.
I had to break up the code into two boxes due to weird Stack Exchange highlighting with the triple quotes.
How would you improve this program on the grounds of:

Readability/cleanness,
Performance, and
Security.

Feel free to include any other comments you have on it that may not fit into those three categories.
import string
import random
import secrets
import argparse
import MyFormatter

alphaL = list(string.ascii_lowercase)
alphaU = list(string.ascii_uppercase)
numeric = list(string.digits)
special = list(&quot;!@#$%^&amp;*&quot;)
special2 = list(&quot;&quot;&quot;~`!@#$%^&amp;*()+=_-{}[]\|:;&quot;'?/&lt;&gt;,.&quot;&quot;&quot;)

parser = argparse.ArgumentParser(
    formatter_class=MyFormatter.MyFormatter,
    description=&quot;Generates a password&quot;,
    usage=&quot;&quot;,
)

parser.add_argument(&quot;-lc&quot;, &quot;--lower&quot;, type=int, default=1, help=&quot;Minimum number of lowercase alpha characters&quot;)
parser.add_argument(&quot;-uc&quot;, &quot;--upper&quot;, type=int, default=1, help=&quot;Minimum number of uppercase alpha characters&quot;)
parser.add_argument(&quot;-n&quot;, &quot;--numeric&quot;, type=int, default=1, help=&quot;Minimum number of numeric characters&quot;)
parser.add_argument(&quot;-s&quot;, &quot;--special&quot;, type=int, default=1, help=&quot;Minimum number of special characters&quot;)
parser.add_argument(&quot;-se&quot;, &quot;--extended&quot;, action = 'store_const', default = False, const= True, help=&quot;Toggles the extendard special character subset. Passwords may not be accepted by all services&quot;)
parser.add_argument(&quot;-l&quot;, &quot;--length&quot;, type=int, default=20, help=&quot;Length of the generated password&quot;)

args = parser.parse_args()

length = args.length

minimums = [
    args.lower,
    args.upper,
    args.numeric,
    args.special,
]

password = &quot;&quot;

for i in range(0, minimums[0]) :
    password += secrets.choice(alphaL)
    
for i in range(0, minimums[1]) :
    password += secrets.choice(alphaU)

for i in range(0, minimums[2]) :
    password += secrets.choice(numeric)

if args.extended :
    subset = alphaL + alphaU + numeric + special2
    for i in range(0, minimums[3]) :
        password += secrets.choice(special2)
elif minimums[3] :
    subset = alphaL + alphaU + numeric + special
    for i in range(0, minimums[3]) :
        password += secrets.choice(special)

for i in range(0, 100) :
    random.shuffle(subset)
            
for i in range(len(password), length) :
    password += secrets.choice(subset)

for i in range(0, 100) :
    password = ''.join(random.sample(password, len(password)))

print(&quot;Password: &quot;, password)
Already pointed out by other users

There is no need to make lists from the strings as any sequence will do
minimums - you make bad names where the existing names were better

The if clause
if args.extended :
    subset = alphaL + alphaU + numeric + special2
    for i in range(0, minimums[3]) :
        password += secrets.choice(special2)
elif minimums[3] :
    subset = alphaL + alphaU + numeric + special
    for i in range(0, minimums[3]) :
        password += secrets.choice(special)

is erroneous. The check for elif minimums[3] shall be replaced by an else:
Also - just because you already switch on args.extended you are not required to pack all variation on that argument in there.
You prepare subset for the logically next function (choice from compound set) before even performing the current one (choice from special).
That results in less readability. There is nothing wrong in switching two times if required. However in this case you should simply switch
if args.extended:
    special = &quot;&quot;&quot;~`!@#$%^&amp;*()+=_-{}[]\|:;&quot;'?/&lt;&gt;,.&quot;&quot;&quot;
else:
    special = &quot;!@#$%^&amp;*&quot;

Then the choice from special/special2 collapses to the standard pattern.
Names
It was already mentioned that the minimum list is a name worse than the args attributes (which could be even more descriptive).
Also the names for your imports are bad and camel-cased. why not simply follow the existing naming scheme?
ascii_lowercase = string.ascii_lowercase

or even more straight
from string import ascii_lowercase
from string import ascii_uppercase
from string import digits

An extra bad name is subset
subset = alphaL + alphaU + numeric + special

which is clearly a union or superset
The standard pattern
You do
for i in range(0, minimums[0]) :
    password += secrets.choice(alphaL)

the recommended way to character wise join a string is
password += &quot;&quot;.join(secrets.choice(ascii_lowercase) for _ in range (0, args.lower)

Cargo cult
You do
for i in range(0, 100) :
    random.shuffle(subset)

which is pointless. It is pointless to shuffle a 100 times. there is no extra quality in randomness compared to a single shuffle.
It is even more pointless to do that before apply secrets.choice(subset). You could safely sort subset before choice().
Final shuffle
You do
for i in range(0, 100):
    print(&quot;Password: &quot;, password)
    password = ''.join(random.sample(password, len(password)))

which I interpret as final shuffle to eliminate the order of the subsets ascii_lowercase, ascii_uppercase, ...
Again you shuffle a 100 times which will not add any value compared to a single shuffle. To be cryptographically safe you should implement a shuffle based on secrets.choice.