Answer in one line:
''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(N))
or even shorter starting with Python 3.6 using random.choices()
:
''.join(random.choices(string.ascii_uppercase + string.digits, k=N))
A cryptographically more secure version: see this post
''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for _ in range(N))
In details, with a clean function for further reuse:
>>> import string
>>> import random
>>> def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
... return ''.join(random.choice(chars) for _ in range(size))
...
>>> id_generator()
'G5G74W'
>>> id_generator(3, "6793YUIO")
'Y3U'
How does it work ?
We import string
, a module that contains sequences of common ASCII characters, and random
, a module that deals with random generation.
string.ascii_uppercase + string.digits
just concatenates the list of characters representing uppercase ASCII chars and digits:
>>> string.ascii_uppercase
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
>>> string.digits
'0123456789'
>>> string.ascii_uppercase + string.digits
'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
Then we use a list comprehension to create a list of 'n' elements:
>>> range(4) # range create a list of 'n' numbers
[0, 1, 2, 3]
>>> ['elem' for _ in range(4)] # we use range to create 4 times 'elem'
['elem', 'elem', 'elem', 'elem']
In the example above, we use [
to create the list, but we don't in the id_generator
function so Python doesn't create the list in memory, but generates the elements on the fly, one by one (more about this here).
Instead of asking to create 'n' times the string elem
, we will ask Python to create 'n' times a random character, picked from a sequence of characters:
>>> random.choice("abcde")
'a'
>>> random.choice("abcde")
'd'
>>> random.choice("abcde")
'b'
Therefore random.choice(chars) for _ in range(size)
really is creating a sequence of size
characters. Characters that are randomly picked from chars
:
>>> [random.choice('abcde') for _ in range(3)]
['a', 'b', 'b']
>>> [random.choice('abcde') for _ in range(3)]
['e', 'b', 'e']
>>> [random.choice('abcde') for _ in range(3)]
['d', 'a', 'c']
Then we just join them with an empty string so the sequence becomes a string:
>>> ''.join(['a', 'b', 'b'])
'abb'
>>> [random.choice('abcde') for _ in range(3)]
['d', 'c', 'b']
>>> ''.join(random.choice('abcde') for _ in range(3))
'dac'
Best Answer
As per SE-0054,
ImplicitlyUnwrappedOptional<T>
is no longer a distinct type; there is onlyOptional<T>
now.Declarations are still allowed to be annotated as implicitly unwrapped optionals
T!
, but doing so just adds a hidden attribute to inform the compiler that their value may be force unwrapped in contexts that demand their unwrapped typeT
; their actual type is nowT?
.So you can think of this declaration:
as actually looking like this:
Only the compiler sees this
@_implicitlyUnwrapped
attribute, but what it allows for is the implicit unwrapping ofstr
's value in contexts that demand aString
(its unwrapped type):But in all other cases where
str
can be type-checked as a strong optional, it will be:And the compiler will always prefer treating it as such over force unwrapping.
As the proposal says (emphasis mine):
When it comes to string interpolation, under the hood the compiler uses this initialiser from the
_ExpressibleByStringInterpolation
protocol in order to evaluate a string interpolation segment:Therefore when implicitly called by your code:
As
str
's actual type isString?
, by default that's what the compiler will infer the generic placeholderT
to be. Therefore the value ofstr
won't be force unwrapped, and you'll end up seeing the description for an optional.If you wish for an IUO to be force unwrapped when used in string interpolation, you can simply use the force unwrap operator
!
:or you can coerce to its non-optional type (in this case
String
) in order to force the compiler to implicitly force unwrap it for you:both of which, of course, will crash if
str
isnil
.