Variables can be seen as containers in computer memory where we can store values. Each such container is given a unique name through which we can reference the value stored in it at any time, this name is called an identifier.

You cannot just give any name to an identifier, there are some rules which one must follow for an identifier to be valid.

Rules of naming identifiers
  1. An identifier cannot be a keyword
  2. Identifiers are case-sensitive
  3. Identifiers can only be made of alphanumeric characters and underscores( A-Z , a-z, 0-9, _)
  4. An identifier can only begin with a letter or an underscore, but not a digit.
  5. White spaces are not allowed in identifiers.
  6. We cannot use special symbols like  ^, %, &, *  e.tc, with the only exception of an underscore( _ ).

Examples of Valid identifiers:

name
first_name
_name
name_1
_NAME1_

Examples of invalid identifiers

1st_name  # identifier cannot start with a digit
name!   # an identifier cannot contain a special character other than the underscore
first name  #white space is not allowed in the identifier

Variable declaration and Assignment

In statically typed languages, a variable is created the moment you declare it by specifying some of its features such as the type of data it is going to hold, but in Python, there is no way of declaring variables explicitly,  instead , the declaration happens automatically  the moment you assign a value to a variable. The syntax of variable  declaration and assignment looks like:

x = 5

In the above example, is the variable's identifier while 5 is the value that will be stored in the variable. The equals symbol ( = ) when used this way is called the assignment operator . 

variable declaration demo

In casual language, we are telling the computer to create a container in the memory,  give that container the name then store an integer in that container, so that from that point onward the computer will associate the variable's name/identifier with the value stored in  it.

x = 5

print( 2 * x )

Variables are called this way because the value stored in a given one can change with time  during the program's execution.

x = 5

x = x * 5

print(x)

In this case the  value  stored in the variable named x will now change to  the value  which was previously there multiplied by 5.

changing the value stored in a variable

You should note that using the variable does  not update it's value unless you explicitly reassign another value to it. example:

x = 25

y = x * 4

print(x)
print(y)

In the above case, the value stored in y becomes  the value of x(25) multiplied by   4 which is 100,  but the value stored in  x which is 25 remain unchanged. 

Multiple Assignment

Python allows assigning a value to multiple variables simultaneously

Example:

a = b = c = 1

All the three variables above ab and will point to the same memory location where an integer object with a value of 1 is stored:

 demonstrating multiple assignment in python variables

Python also allows assignment in one line where each variable is assigned it's own memory allocation,

Example:

a, b, c = 1, 2, 3

print(a)
print(b)
print(c)

In this case, three integer objects will be created in memory:

Multi[ple assignment in Python

When you use the one line  assignment, the number of variables on the left side should be equal to the number of  objects in the right side of the assignment operator otherwise an error will be raised.

Wrong:

a, b, c = 1

Wrong:

a, b, c = 1, 2

Wrong:

a, b = 1, 2, 3

Variables and Types

In statically typed  languages  such as Java or C++ the  type of value that a variable can hold  cannot be changed, this means that if you declare a variable to hold an integer type, you must strictly assign an integer value to it,  trying to assign a value belonging to another type will raise errors. Python on the other hand, is a dynamically typed language, this means that a variable, regardless the type of the  value which was initially stored in it, can hold values belonging to literally any other data types. For example, we can make our variable to store a string despite it previously holding integer values.

x = 5
print(x)

x = "Hello, Variables!"

print(x)

The type() function 

Since the type of the value stored in a python variable can change with time, we might sometimes want to resolve the  type a variable is holding at a given time,  this  builtin function type()  when used on variables  returns the type of the value stored in it. 

x = 'Python'

y = [1, 2, 3, 4]

print(type(x))
print(type(y))

Type Casting  

Casting in Python means to explicitly  change the type of an object to another type. Not all types in Python types are congruent, some can be casted to other types  for example it is possible to change all integers to strings,  on the other hand some strings cannot be casted to an equivalent integer type.

We can use type casting to change the type that a variable is holding  or to change the type of a value before we assign it to a variable,  an example of this is when we want to get an integer input from user but Python's inputs are always of type str we will , therefore, have to explicitly cast the input to integer type.

x = int(input("Enter x:"))
//30_
y = int(input("Enter y:"))
//40
print(x + y)
//70

The int() around the input() is used to cast the input string into an integer.

Casting in Python is achieved by passing the value whose type we want to change , to the target type's constructor function . 

changing string type to list

x = "Python"
x = list(x)
print(x)

Variable Scope

Scope of a variable establishes locations  in a program where  that variable is accessible, by default a Python variable is only accessible from the block it is created.   There are two major scopes , the local scope  and the global scope .

Local Scope:

A variable declared inside a function is local to that function, by default It cannot be accessed outside the func5.

def func():
    x =  150
    print(x)
func()

The variable is also available to the blocks inside the function

def func():
    x =  150

    def inner():
        print(x)
    inner()

func()

Trying to access a variable inside a function from outside that function will not work:

def func1():
   x = 150

def func2():
   print(x)

func2()

Global Scope

Global scope is the top level of all the Python scopes, variables declared at this scope are accessible from anywhere in the program.

x = 900

def func():
   print(x)

func()
x = 900

def func():

    def inner():
        print(x)
    inner()

func()

Overriding Variables

If you create a local variable which shares the same name as a  global one, you effectively override the global variable within that particular block.

x = 900

def func():
    x = 700 #This overrides the global variable within this function x 
    print(x)

func()

print(x)#this points to the global variable

Local to Global

Python offers a builtin keyword called global , this keyword can be used to make a variable created in a local scope  become a global variable.

def func():
   global x
   x= 800
func()

print(x)

If you use the global statement when overriding a variable, the change will take effect even in global scope, you can use it this way to alter  global variables

x = 100
def func():
   global x
   x = 600

func()

print(x)

Good Practices with Python Variables

Let us explore some good practices that we can follow when interacting with variables to ensure that we stick to the  Pythonic way of doing things.

Use descriptive identifiers when naming variables

When naming variables, you should avoid one-letter or short  names which do not state clearly what value that variable holds. Consider if we want to store the age of a user:

bad:

a = int(input("Enter your age:"))

good:

age = int(input("Enter your age:"))

 The only point where one letter variables might be used is in loops to avoid redundancy.

bad:

usn = input("Enter your username:")

good:

username = input("Enter your username:")
multiple word identifiers

When working with identifiers which are made of two or more parts you can use an underscore between the parts to  improve the identifier's clarity. for example:

first_name = input("Enter your first name:")

this type of naming is sometimes referred to as the pothole case.

Another popular naming method for this kind of identifiers is called the camel case , in this case, we capitalize the subsequent parts, our example above in camel case would look like:

firstName = input("Enter your first name:")

Choose the case which you find more convenient when you want to combine two or more words to make an identifier

Upper case for constant declaration.

Constants, unlike variables, do not change throughout the program's execution. Some languages like  C++ and Java provide support for constant values. In such languages a value defined  as a constant is read-only and trying to change it's value will result in an error being raised.

Python does not provide a specific keyword or syntax for defining constants. It is a convention to declare Constants using all capital letters as variable name. Example:

PI = 3.14

WIDTH = 100

This is just a convention, the variable does not  become a read only like in other languages, its value can still be changed normally. Using upper case for constant's name improves program consistency  and readability and also someone else reading your source code will easily identify such variables as constants. 

Lastly, one should be careful when using the lower case letter (l)  and uppercase letter ( O ) in identifiers. These letters can in some cases be mistaken for numbers ( 1 ) and ( 0 ) respectively.