current_month = "MAY"
print(current_month)

"""
Explanation: Python Identifiers aka Variables

In Python, variable names are kind of tags/pointers to the memory location which hosts the data. We can also think of it as a labeled container that can store a single value. That single value can be of practically any data type.
Storing Values in Variables:
In Python, the declaration & assignation of value to the variable are done at the same time, i.e. as soon as we assign a value to a non-existing or existing variable, the required memory location is assigned to it and proper data is populated in it.

NOTE: Storing Values in Python is one of the most important concepts and should be understood with great care.
End of explanation
"""


current_month = "MAY" # A comment.
date = 10

"""
Explanation: In the above example, current_month is the variable name and "MAY" is the value associated with it. Operation performed in the first line is called assignment and such statements are called assignment statements. Lets discuss them in details.
Assignment Statements
You’ll store values in variables with an assignment statement. An assignment statement consists of a variable name, an equal sign (called the assignment operator), and the value to be stored. If you enter the assignment statement current_month = "MAY", then a variable named current_month will be pointing to a memory location which has the string value "MAY" stored in it.

In Python, we do not need to declare variable explicitly. They are declared automatically when any value is assigned. The assignment is done using the equal (=) operator as shown in the below example:
End of explanation
"""


current_month = "JUNE"

"""
Explanation: The pictorial representation of variables from above example.
<img src="files/variables.png">
Now lets perform some actions on the variable current_month and observe the changes happening on it. 
In the example shown below, we will reassign a new value JUNE to the variable current_month and observe the effects of it. 
Image below shows the process of re-assignation. You will note that a new memory is assigned to the variable instead of using the existing one.
End of explanation
"""


current_month = "JUNE"
print(id(current_month))

next_month = "JUNE"
print(id(next_month))

next_month = "June"
print(id(next_month))

"""
Explanation: current_month was initially pointing to memory location containing value MAY and after reassination, it was pointing to a new memory location containing value JUNE and if no other referencing the previous value, then automatically Python GC will clean it at some future time.
End of explanation
"""


########## Reference count ###################
# NOTE: Please test the below code by saving 
#       it as a file and executing it instead
#       of running it here.
#############################################
import sys

new_var = 10101010101000
print(sys.getrefcount(new_var))

"""
Explanation: Note: That value of MAY has not updated but a new memory was allocated for value JUNE and varialbe now points to it.

Later in the chapter, we will show the above senario with more examples.
How to find the reference count of a value
End of explanation
"""


x=y=z=1000  
print(x, y, z)

"""
Explanation: NOTE:
The value of refcount will almost always be more than you think. It is done internally by python to optimize the code. I will be adding more details about it in "Section 2 -> Chapter: GC & Cleanup"

Multiple Assignment:
In multiple assignment, multiple variables are assigned values in a single line. There are two ways multiple assignment can be done in python. In first format all the variables point to the same value and in next all variables point to individual values. 
1. Assigning single value to multiple variables:
End of explanation
"""


print(id(x))
print(id(y))
print(id(z))

"""
Explanation: In the above example, all x, y and z are pointing to same memory location which contains 1000, which we are able to identify by checking the id of the variables. They are pointing to the same memory location, thus value of id for all three are same.
End of explanation
"""


x = 200
print(x)  
print(y) 
print(z)  
print(id(x))
print(id(y))
print(id(z))

"""
Explanation: Now, lets change value of one varialbe and again check respective ides.
End of explanation
"""


### INTEGER 
x=1000
y=1000
z=1000  
print(x)  
print(y) 
print(z)  
print(id(x))
print(id(y))
print(id(z))

### INTEGER 
x=24
y=24
z=24  
print(x)  
print(y) 
print(z)  
print(id(x))
print(id(y))
print(id(z))

### String
x="1000"
y=1000
z="1000"  
print(x)  
print(y) 
print(z)  
print(id(x))
print(id(y))
print(id(z))

"""
Explanation: Now, lets test something else. Can different data types impact the behavior of python memory optimization. We will first test it with integer, string and then with list.
End of explanation
"""


### list
x = ["1000"]
y = [1000]
z = ["1000"]  
a = [1000]
print(x)  
print(y) 
print(z)  
print(a)  
print(id(x))
print(id(y))
print(id(z))
print(id(a))

"""
Explanation: check the id of both x and z, they are same but y is not same.
End of explanation
"""


x, y, z = 10, 20, 30
print(x)  
print(y) 
print(z)  
print(id(x))
print(id(y))
print(id(z))

x, y, z = 10, 120, 10
print(x)  
print(y) 
print(z)  
print(id(x))
print(id(y))
print(id(z))

"""
Explanation: 2. Assigning multiple values to multiple variables:
End of explanation
"""


pm_name = "Narendra Modi"
prime_minister = "Narendra Modi"
cong_p_name = "Rahul Gandhi"
corrent_name_of_cong_president = "Rahul Gandhi"
cong_president = "Rahul Gandhi"
cname = "RG"

"""
Explanation: Variable Names & Naming Conventions
There are a couple of naming conventions in use in Python:
- lower_with_underscores: Uses only lower case letters and connects multiple words with underscores.
- UPPER_WITH_UNDERSCORES: Uses only upper case letters and connects multiple words with underscores.
- CapitalWords: Capitalize the beginning of each letter in a word; no underscores. With these conventions in mind, here are the naming conventions in use.

Variable Names: lower_with_underscores
Constants: UPPER_WITH_UNDERSCORES
Function Names: lower_with_underscores
Function Parameters: lower_with_underscores
Class Names: CapitalWords
Method Names: lower_with_underscores
Method Parameters and Variables: lower_with_underscores
Always use self as the first parameter to a method
To indicate privacy, precede name with a single underscore.
End of explanation
"""


this_is_my_number 
THIS_IS_MY_NUMBER  
ThisIsMyNumber
this_is_number 
anotherVarible
This1
this1home
1This
__sd__
_sd

"""
Explanation: Options can be used to override the default regular expression associated to each type. The table below lists the types, their associated options, and their default regular expressions.
|        Type       |         Default Expression        |
|:-----------------:|:-----------------------------------------:|
|      Argument     | [a-z_][a-z0-9_]                    |
|     Attribute     | [a-z_][a-z0-9_]                    |
|       Class       | [A-Z_][a-zA-Z0-9]                       |
|      Constant     | (([A-Z_][A-Z0-9_]            |
|      Function     | [a-z_][a-z0-9_]                    |
|       Method      | [a-z_][a-z0-9_]                    |
|       Module      | (([a-z_][a-z0-9_]), ([A-Z][a-zA-Z0-9])) |
|      Variable     | [a-z_][a-z0-9_]                    |
| Variable, inline1 | [A-Za-z_][A-Za-z0-9_]                   |
Please find the invalid variables name from the below list
End of explanation
"""


_ is used 
* To use as ‘Internationalization(i18n)’ or ‘Localization(l10n)’ functions.

"""
Explanation: Good Variable Name

Choose meaningful name instead of short name. roll_no is better than rn.
Maintain the length of a variable name. Roll_no_of_a_student is too long?
Be consistent; roll_no or RollNo
Begin a variable name with an underscore(_) character for a special case.

Exercises
Q 1. Find the valid and in-valid variable names from the followings:

balance
current-balance 
current balance 
current_balance 
4account 
_spam 
42 
SPAM 
total_$um 
account4 
'hello'  

Q 2. Multiple Choice Questions & Answers


Is Python case sensitive when dealing with identifiers?
a) yes
b) no
c) machine dependent
d) none of the mentioned


What is the maximum possible length of an identifier?
a) 31 characters
b) 63 characters
c) 79 characters
d) none of the mentioned


What does local variable names beginning with an underscore mean?
a) they are used to indicate a private variables of a class
b) they confuse the interpreter
c) they are used to indicate global variables
d) None of the 


Which of the following is true for variable names in Python?
a) unlimited length
b) Only _ and $ special characters allowed in variable name
c) private members should have leading & trailing underscores
d) None of the above
End of explanation
"""


