Python / Objects and Variables

In Python, an object represents data with a value and supported behavior; a variable is a name bound to an object. x = 100 binds x to an integer object, and assigning another value changes what x refers to. Assignment itself does not copy objects, so multiple names can share the same list.

Names, objects and types

The Python data model describes identity, type and value as properties of objects. Type determines supported operations and behavior. Identity stays fixed during an object's lifetime; whether its value can change depends on its type. Treating a variable as a permanently typed storage box makes rebinding and sharing difficult to explain.

These examples use Python 3 and the standard library. Their outputs were checked on Python 3.12.14. Save each complete example as UTF-8 in objects_demo.py and run python objects_demo.py in a terminal, or python3 if that is your interpreter name. Replace the file contents for each independent example.

value = 100
print(value, type(value).__name__)
value = "hello"
print(value, type(value).__name__)
100 int
hello str

The name value first refers to an int, then to a str. The integer object 100 has not changed its type. An assignment statement evaluates the right-hand expression and binds the result to the name on the left. type(value) reports the type of the object currently bound to that name.

Sharing a list versus rebinding a name

second = first makes both names refer to the same list, rather than creating a copy. Compare changing that shared object with rebinding one name:

first = [1, 2]
second = first
second.append(3)
print(first)
print(second)
print(first is second)
first = [0]
print(first)
print(second)
print(first is second)
[1, 2, 3]
[1, 2, 3]
True
[0]
[1, 2, 3]
False

append changes the shared list, so reading first also shows the added 3. In contrast, first = [0] binds only first to a new list. second still refers to the old one. Distinguish changing a name's binding from changing the object both names refer to.

Operation Meaning Effect on another shared name
second = first Add a name for the same object Both observe later mutations
second.append(3) Change the list object Reading the same list shows the change
first = [0] Bind first to another object second keeps its binding

Immutable objects and +=

Integers, strings and tuples are immutable; lists and dictionaries are mutable. Immutable means that the object cannot be changed in place, not that its name cannot be assigned another value.

first = 10
second = first
first += 1
print(first)
print(second)
11
10

The integer 10 is not modified into 11. first is rebound to the addition result while second still refers to 10. With a list, the same += syntax can modify the existing object, so do not generalize its behavior across all types. A tuple's own item references cannot be replaced, but a list contained within a tuple can still change.

Choose the necessary copying depth

A list's copy() creates a new outer list: a shallow copy. Nested objects remain shared. Compare that with copy.deepcopy() using the same starting data:

from copy import deepcopy

original = [[1], [2]]
shallow = original.copy()
deep = deepcopy(original)
shallow[0].append(9)
shallow.append([3])
print(original)
print(shallow)
print(deep)
print(original is shallow)
print(original[0] is shallow[0])
[[1, 9], [2]]
[[1, 9], [2], [3]]
[[1], [2]]
False
True

Appending 9 to the inner list through shallow also changes what original shows. Appending [3] to the outer shallow list does not increase original's length. deep retains an independent nested copy of the initial contents. Decide whether sharing is intentional and copy only what needs independence. Deep copying has costs and is not a way to duplicate resources such as files or sockets as ordinary data.

Equality and identity answer different questions

For built-in lists, == compares contents and is compares whether they are the very same object. These lists have equal contents but were created separately:

left = [1, 2]
right = [1, 2]
alias = left
print(left == right)
print(left is right)
print(left is alias)
print(id(left) == id(alias))
missing = None
print(missing is None)
True
False
True
True
True

Use the comparison operators for their intended purpose. id() returns an integer representing identity; the actual number can vary between runs and is not guaranteed to be a memory address in every Python implementation. Do not compare numeric or string values with is based on caching or reuse. Use == for values and is None when checking for None.

del removes a name binding

del is a statement, not a function. del first removes that name's binding. An object remains usable through another name that still refers to it:

first = [1, 2]
second = first
del first
print(second)
try:
    print(first)
except NameError:
    print("first: NameError")
try:
    del unknown_name
except NameError:
    print("unknown_name: NameError")
[1, 2]
first: NameError
unknown_name: NameError

second still prints the list, while reading first raises NameError. Deleting an unknown name also raises NameError. The del statement can delete an item or attribute when given a different target; not every del operation removes a name. Deleting one name does not guarantee immediate destruction or memory reclamation. Close files with with or close rather than relying on del.

Choose valid, readable variable names

Names follow identifier rules. For basic English characters, start with a letter or underscore and use digits only after the first character. Spaces and hyphens are not allowed, and case matters. Python also permits specified Unicode characters, so identifiers are not restricted to English letters.

import keyword

for name in ["user_name", "2name", "class", "first-name", "café", "match"]:
    valid = name.isidentifier() and not keyword.iskeyword(name)
    print(name, valid)
print("match soft keyword:", keyword.issoftkeyword("match"))
user_name True
2name False
class False
first-name False
café True
match True
match soft keyword: True

isidentifier() alone accepts the shape of class, so also check keyword.iskeyword(). In this environment, match is a soft keyword with special meaning in certain contexts, not a universally reserved keyword. Consult the identifier and keyword grammar for these context rules. Keyword lists can change by version; use the running interpreter's keyword module.

A syntactically valid name such as list or str can still hide a built-in and make it harder to call. Prefer names describing a value's role, such as user_name and item_count. When NameError occurs, check capitalization, whether assignment happened, and the scope in which the name is available.