By the end of this chapter, you should understand:
- What an attribute is.
- How instance attributes are stored.
- How class attributes are stored.
- How Python looks up attributes.
- Why attribute lookup and attribute assignment are different.
- How instance attributes can shadow class attributes.
- How methods are functions stored on classes.
- What a bound method is.
- How
selfis passed automatically. - Why calling
obj.method()differs from callingClass.method(obj). - How
getattr(),setattr(),hasattr(), anddelattr()work. - Why mutable class attributes are dangerous when used as per-instance state.
- How attribute lookup prepares us for properties, descriptors, inheritance, and MRO.
Chapter 43 introduced classes and instances.
Now we zoom in on the most important operation in object-oriented Python:
attribute access
Every time you write:
user.name
user.greet()
config.DEBUG
math.sqrtPython is doing attribute lookup.
Understanding this one idea unlocks a surprising amount of advanced Python.
Methods, class attributes, instance attributes, properties, descriptors, inheritance, super(), and the data model all depend on attribute lookup.
This chapter is where the machinery starts to become visible.
An attribute is a name attached to an object.
Example:
class User:
def __init__(self, name):
self.name = nameCreate an instance:
user = User("Ada")Access:
print(user.name)Here:
name
is an attribute of the user object.
Conceptually:
user instance namespace:
name -> "Ada"
The expression:
user.namemeans:
look up attribute name on object user
Attributes are how objects expose state and behavior.
State:
user.nameBehavior:
user.greet()Both use attribute lookup.
Dot syntax:
object.attributemeans:
find attribute on object
Examples:
user.name
account.balance
task.done
math.sqrt
settings.DEBUGThe object before the dot can be:
- An instance.
- A class.
- A module.
- A package.
- A function.
- Almost any Python object.
Dot syntax is everywhere because Python's object model is everywhere.
Example:
import math
print(math.pi)
print(math.sqrt(25))math is a module object.
pi and sqrt are attributes of that module.
Same syntax:
module.attribute
instance.attribute
class.attributeThe details differ, but the idea is the same:
attribute lookup on an object
Instance attributes belong to individual instances.
Example:
class User:
def __init__(self, name, email):
self.name = name
self.email = emailCreate:
ada = User("Ada", "ada@example.com")
grace = User("Grace", "grace@example.com")Each instance has its own attributes:
ada:
name -> "Ada"
email -> "ada@example.com"
grace:
name -> "Grace"
email -> "grace@example.com"
Use:
print(ada.name)
print(grace.name)Output:
Ada
Grace
Same attribute name.
Different instance namespaces.
This is why classes can create many objects with the same structure but different state.
For ordinary Python instances, instance attributes are stored in a dictionary called __dict__.
Example:
class User:
def __init__(self, name, email):
self.name = name
self.email = email
user = User("Ada", "ada@example.com")
print(user.__dict__)Output:
{'name': 'Ada', 'email': 'ada@example.com'}
You can also use:
print(vars(user))Output:
{'name': 'Ada', 'email': 'ada@example.com'}
For many ordinary classes:
vars(user) shows the instance namespace
This is not true for every possible object.
Some classes use __slots__.
Some objects are implemented in C.
Some objects customize attribute access.
But for normal beginner-defined classes, __dict__ is the right model.
Class attributes belong to the class object.
Example:
class User:
role = "member"
def __init__(self, name):
self.name = nameThe class namespace contains:
role -> "member"
__init__ -> function object
Access through the class:
print(User.role)Output:
member
Access through an instance:
ada = User("Ada")
print(ada.role)Output:
member
Why does ada.role work?
Because if Python does not find role on the instance, it can look on the class.
This is attribute lookup.
Class attributes are stored in the class namespace.
Example:
class User:
role = "member"
def greet(self):
return "hello"Inspect:
print(User.__dict__)You will see a mapping containing names such as:
role
greet
__module__
__dict__
__weakref__
__doc__
Some names are added by Python automatically.
You can also use:
print(vars(User))The important part:
role -> "member"
greet -> function object
A class is an object with a namespace.
Class attributes live there.
Instance attributes live on instances.
For a simple instance attribute lookup:
obj.namePython roughly asks:
1. Does the instance have this attribute?
2. If not, does the class have this attribute?
3. If not, do parent classes have this attribute?
4. If not, raise AttributeError.
This is simplified.
Descriptors can change the order.
We will study descriptors later.
For now, the beginner model is:
instance first
class next
inherited classes later
Example:
class User:
role = "member"
ada = User()
ada.name = "Ada"Lookup:
ada.namefinds name on the instance.
Lookup:
ada.roledoes not find role on the instance.
Then it finds role on the class.
Code:
class User:
role = "member"
def __init__(self, name):
self.name = name
ada = User("Ada")Namespaces:
ada instance:
name -> "Ada"
User class:
role -> "member"
__init__ -> function object
Expression:
ada.nameLookup:
find name on ada instance -> yes -> "Ada"
Expression:
ada.roleLookup:
find role on ada instance -> no
find role on User class -> yes -> "member"
Expression:
ada.missingLookup:
find missing on ada instance -> no
find missing on User class -> no
raise AttributeError
This is crucial.
Attribute lookup:
ada.rolecan search the instance and then the class.
Attribute assignment:
ada.role = "admin"usually writes to the instance.
It does not search for role on the class and modify it.
Example:
class User:
role = "member"
ada = User()
grace = User()
ada.role = "admin"
print(ada.role)
print(grace.role)
print(User.role)Output:
admin
member
member
Why?
Assignment created:
ada instance:
role -> "admin"
The class still has:
User class:
role -> "member"
Lookup and assignment are different operations.
Do not blur them.
When an instance attribute has the same name as a class attribute, it shadows the class attribute for that instance.
Example:
class User:
role = "member"
ada = User()
grace = User()
ada.role = "admin"Now:
ada.role
finds the instance attribute first:
admin
But:
grace.role
does not find role on grace, so it finds the class attribute:
member
The class attribute still exists:
print(User.role)Output:
member
Shadowing means:
a nearer namespace has a name that hides a farther namespace's name during lookup
This is the same concept we saw with local variables shadowing globals and built-ins.
Example:
class User:
role = "member"
ada = User()
ada.role = "admin"
print(ada.role)
del ada.role
print(ada.role)Output:
admin
member
What happened?
Before deletion:
ada instance:
role -> "admin"
User class:
role -> "member"
After:
del ada.rolethe instance attribute is removed.
Now lookup:
ada.roledoes not find role on the instance.
So it finds role on the class.
Deletion did not create the class attribute.
It revealed the one that was already there.
Assignment and mutation are different.
Consider:
class Team:
members = []
red = Team()
blue = Team()
red.members.append("Ada")
print(blue.members)Output:
['Ada']
Why?
Lookup:
red.membersdoes not find members on red.
It finds members on the class.
That object is a list.
Then:
.append("Ada")mutates the list.
It does not assign a new members attribute to red.
So both instances still see the shared class list.
This is the classic mutable class attribute trap.
Compare:
class Team:
members = []
red = Team()
blue = Team()
red.members = ["Ada"]Now:
print(red.members)
print(blue.members)
print(Team.members)Output:
['Ada']
[]
[]
Why?
Assignment:
red.members = ["Ada"]creates an instance attribute on red.
It does not mutate Team.members.
Namespaces:
red instance:
members -> ["Ada"]
blue instance:
no members
Team class:
members -> []
blue.members still finds the class list.
This contrast is vital:
red.members.append(...) -> lookup then mutate found object
red.members = ... -> assign attribute on red
If each instance needs its own list, create the list in __init__.
Correct:
class Team:
def __init__(self, name):
self.name = name
self.members = []
def add_member(self, member):
self.members.append(member)Use:
red = Team("Red")
blue = Team("Blue")
red.add_member("Ada")
print(red.members)
print(blue.members)Output:
['Ada']
[]
Each instance has its own namespace:
red:
name -> "Red"
members -> red's list
blue:
name -> "Blue"
members -> blue's list
Rule:
mutable state that belongs to one object should usually be created on that object
A method is found through attribute lookup.
Example:
class User:
def greet(self):
return "hello"
user = User()Call:
user.greet()Before Python calls anything, it must evaluate:
user.greetThat is attribute lookup.
Where is greet stored?
In the class namespace:
User class:
greet -> function object
When accessed through the instance, Python returns a bound method:
method = user.greet
print(method)The bound method remembers both:
the function
the instance
Then:
method()passes the instance as self.
Inside a class body:
class User:
def greet(self):
return "hello"the def statement creates a function object.
That function object is stored in the class namespace under the name greet.
Conceptually:
User class namespace:
greet -> function object
Access through the class:
print(User.greet)You see a function.
Access through an instance:
user = User()
print(user.greet)You see a bound method.
The function did not change permanently.
Attribute access transformed it for this access.
That transformation is part of the descriptor protocol.
We will study descriptors later.
For now:
class access gives function
instance access gives bound method
A bound method is a callable object that remembers an instance.
Example:
class User:
def greet(self):
return f"hello from {self.name}"
def __init__(self, name):
self.name = name
user = User("Ada")
method = user.greetNow:
print(method())Output:
hello from Ada
The method object remembers:
function -> User.greet
self -> user
You can inspect:
print(method.__self__)
print(method.__func__)__self__ is the bound instance.
__func__ is the original function.
These details are not used every day.
But they prove the model:
bound method = function + instance
These are usually equivalent for ordinary instance methods:
obj.method()and:
Class.method(obj)Example:
class User:
def greet(self):
return f"hello {self.name}"
def __init__(self, name):
self.name = name
ada = User("Ada")
print(ada.greet())
print(User.greet(ada))Both output:
hello Ada
Why?
ada.greet()gets a bound method that already remembers ada.
User.greet(ada)gets the raw function from the class and passes ada manually.
This explains self.
self is not magic inside the function.
It is just the first parameter.
The method call syntax supplies it automatically.
Method lookup follows attribute lookup rules.
Example:
class User:
def describe(self):
return "user"
user = User()When you call:
user.describe()Python:
1. looks for describe on user instance
2. if not found, looks on User class
3. finds function object
4. binds it to user
5. calls bound method
This means methods are not a separate lookup category.
They are attributes that happen to be callable and bind specially when they are functions stored on classes.
This is one of Python's elegant unifications:
data attribute lookup
method lookup
module attribute lookup
class attribute lookup
all use the attribute model.
Because methods are attributes, you can shadow a method on a single instance.
Example:
class User:
def greet(self):
return "hello"
user = User()
user.greet = lambda: "custom hello"
print(user.greet())Output:
custom hello
What happened?
The instance now has an attribute named greet.
Lookup finds the instance attribute before the class method.
Namespace:
user instance:
greet -> lambda function
User class:
greet -> original function
This is flexible.
It can also be confusing.
Do not usually replace methods on individual instances in ordinary application code.
But knowing it is possible helps explain how attribute lookup works.
If an instance attribute is callable, you can call it.
Example:
class Button:
pass
button = Button()
button.action = lambda: "clicked"
print(button.action())Output:
clicked
But action is not a normal bound method.
It is just a function object stored directly on the instance.
It does not automatically receive self.
Example:
button.action = lambda self: "clicked"
button.action()This fails because no self is passed automatically for plain functions stored on instances.
Automatic method binding happens for functions found on classes.
This distinction matters.
Attribute assignment:
obj.name = valueusually stores a name on the object.
Example:
class User:
pass
user = User()
user.name = "Ada"Now:
print(user.__dict__)Output:
{'name': 'Ada'}
For ordinary instances, assignment writes to the instance namespace.
But there are advanced exceptions:
- Properties can intercept assignment.
- Descriptors can intercept assignment.
__setattr__can customize assignment.__slots__can restrict available attributes.
We will study these later.
For now:
obj.name = value
usually creates or updates an instance attribute.
Attribute deletion:
del obj.nameusually removes an attribute from the object.
Example:
class User:
pass
user = User()
user.name = "Ada"
print(user.name)
del user.name
print(user.name)The last line raises:
AttributeError
because the instance attribute no longer exists.
If a class attribute with the same name exists, deletion can reveal it:
class User:
role = "member"
user = User()
user.role = "admin"
del user.role
print(user.role)Output:
member
Again:
delete instance attribute
then lookup can find class attribute
getattr() gets an attribute by name.
Example:
class User:
def __init__(self, name):
self.name = name
user = User("Ada")
print(getattr(user, "name"))Output:
Ada
This is equivalent to:
user.namewhen the attribute name is known in code.
getattr() is useful when the attribute name is dynamic:
field = "name"
print(getattr(user, field))You can provide a default:
print(getattr(user, "email", "missing"))Output:
missing
Without a default, missing attributes raise AttributeError.
setattr() sets an attribute by name.
Example:
class User:
pass
user = User()
setattr(user, "name", "Ada")
print(user.name)Output:
Ada
This is equivalent to:
user.name = "Ada"when the attribute name is known in code.
setattr() is useful for dynamic names:
field = "email"
value = "ada@example.com"
setattr(user, field, value)Use it carefully.
Dynamic attribute creation can make objects harder to understand if overused.
Clear explicit attributes are usually better.
hasattr() checks whether an object has an attribute.
Example:
class User:
pass
user = User()
user.name = "Ada"
print(hasattr(user, "name"))
print(hasattr(user, "email"))Output:
True
False
At a high level:
hasattr(obj, name)tries to get the attribute.
If lookup succeeds, it returns True.
If lookup raises AttributeError, it returns False.
This means hasattr() can trigger custom attribute logic.
For ordinary objects, that is fine.
For advanced objects with properties or custom __getattr__, remember that attribute checks may run code.
delattr() deletes an attribute by name.
Example:
class User:
pass
user = User()
user.name = "Ada"
delattr(user, "name")
print(hasattr(user, "name"))Output:
False
This is equivalent to:
del user.namewhen the attribute name is known.
Use delattr() for dynamic names:
field = "name"
delattr(user, field)Again, dynamic attribute manipulation is powerful.
It should be used when the design calls for it, not as a default habit.
dir() lists names available on an object.
Example:
class User:
role = "member"
def __init__(self, name):
self.name = name
def greet(self):
return "hello"
user = User("Ada")
print(dir(user))You may see names such as:
name
role
greet
__class__
__dict__
...
dir() includes more than instance attributes.
It includes names available through the object's type and inheritance.
Use:
vars(user)to see direct instance attributes.
Use:
dir(user)to inspect available names more broadly.
If an attribute is not found, Python raises AttributeError.
Example:
class User:
pass
user = User()
print(user.name)Error:
AttributeError
Namespace explanation:
Python looked for name on the instance
then on the class
then inherited classes
it did not find name
Compare with:
print(name)That raises:
NameError
because plain name lookup failed.
Different operation:
name -> plain name lookup
user.name -> attribute lookup
Different error:
NameError
AttributeError
We have mostly discussed one class.
Inheritance adds parent classes.
Example:
class Animal:
def speak(self):
return "sound"
class Dog(Animal):
pass
dog = Dog()
print(dog.speak())Output:
sound
Where did speak come from?
Not from the dog instance.
Not from the Dog class.
It came from the parent class Animal.
Inheritance extends attribute lookup:
instance
class
parent classes
For multiple inheritance, the order becomes more interesting.
That is MRO.
We will study it later.
Our simplified model says:
instance first
class next
But descriptors can alter this.
Properties, methods, static methods, class methods, and many advanced Python features rely on descriptors.
Example:
class User:
@property
def name(self):
return "Ada"Here:
user.namelooks like ordinary attribute access.
But it runs a method behind the scenes.
That is descriptor behavior.
We are not studying descriptors fully yet.
But remember:
attribute lookup is customizable
This is why attribute lookup is central to advanced Python.
Properties let method logic appear as attribute access.
Example:
class Rectangle:
def __init__(self, width, height):
self.width = width
self.height = height
@property
def area(self):
return self.width * self.heightUse:
rectangle = Rectangle(10, 5)
print(rectangle.area)No parentheses.
It looks like data.
But Python calls the property's getter method.
This is possible because attribute lookup has hooks.
Chapter 45 will introduce managed attributes and encapsulation.
Chapter 54 will go deeper into descriptors.
For now, see properties as a preview of the power hidden behind dot syntax.
Properties can also manage assignment.
Example idea:
class User:
@property
def age(self):
...
@age.setter
def age(self, value):
...Then:
user.age = 30may run validation code.
This is different from ordinary instance attribute assignment.
So far, our simple rule has been:
obj.name = value writes to obj
That is true for ordinary attributes.
But managed attributes can intercept assignment.
This is why we are moving from:
basic attributes
to:
managed attributes
in the next chapter.
When you expose an attribute, you expose part of an object's interface.
Example:
class User:
def __init__(self, name):
self.name = nameOther code may use:
user.nameThat becomes part of how the object is used.
If later you rename it:
user.full_nameyou may break callers.
Attributes are not just implementation details once other code depends on them.
Object API includes:
- Attributes callers read.
- Attributes callers assign.
- Methods callers call.
- Exceptions methods raise.
- Behavior methods promise.
Designing attributes is designing an interface.
Python uses naming conventions.
Public attribute:
user.nameInternal-looking attribute:
user._nameThe leading underscore means:
this is intended for internal use
It is not strict privacy.
Other code can still access:
user._nameBut the convention says:
do not rely on this from outside the class
This convention matters because Python does not force heavy access restrictions.
Python relies on clear interfaces and responsible usage.
Chapter 45 will discuss encapsulation more carefully.
Class:
class User:
role = "member"
def __init__(self, name, email):
self.name = name
self.email = email
def display(self):
return f"{self.name} <{self.email}>"Use:
ada = User("Ada", "ada@example.com")Namespaces:
ada instance:
name -> "Ada"
email -> "ada@example.com"
User class:
role -> "member"
__init__ -> function object
display -> function object
Lookups:
ada.namefinds instance attribute.
ada.rolefinds class attribute.
ada.displayfinds function on class and returns bound method.
ada.display()calls the bound method.
This one example shows the whole chapter in miniature.
Class:
class Counter:
step = 1
def __init__(self):
self.value = 0
def increment(self):
self.value += self.step
return self.valueUse:
counter = Counter()
print(counter.increment())
print(counter.increment())Output:
1
2
Lookup inside increment:
self.valuefinds instance attribute.
self.stepdoes not find step on the instance, so it finds class attribute.
Now:
counter.step = 5This creates an instance attribute.
Next:
print(counter.increment())uses instance step, not class step.
This is a practical use of class defaults that instances can override.
Class attributes can represent shared data when sharing is intentional.
Example:
class PluginRegistry:
plugins = {}
@classmethod
def register(cls, name, plugin):
cls.plugins[name] = pluginWe have not deeply studied @classmethod yet.
But the idea is:
plugins belongs to the class, not one instance
Shared class attributes are appropriate when the data is truly shared.
Bad:
per-team members list stored on Team class
Good:
one registry shared by the registry class
The issue is not mutability itself.
The issue is accidental sharing.
Mutable class attributes are fine when shared mutation is the design.
They are dangerous when each instance should have its own state.
Example:
class User:
role = "member"
user = User()
user.role = "admin"This does not update:
User.roleIt creates or updates:
user.roleCheck:
print(User.role)
print(user.role)Output:
member
admin
If you want to update the class attribute:
User.role = "admin"Then instances without their own role will see the new class value.
Attribute assignment target matters.
Bad:
class Cart:
items = []
def add(self, item):
self.items.append(item)Every cart shares the same list.
Correct:
class Cart:
def __init__(self):
self.items = []
def add(self, item):
self.items.append(item)Now each cart has its own list.
Ask:
Should this data be shared by all instances?
If yes, class attribute may be right.
If no, use instance attribute.
Beginners often think methods are stored on each instance.
For normal methods, they are not.
Example:
class User:
def greet(self):
return "hello"The function greet is stored on the class.
Instances access it through attribute lookup.
This means:
User.greetand:
user.greetare related but not identical.
Class access gives the function.
Instance access gives a bound method.
This is why self appears automatically in normal method calls.
Example:
class User:
def greet(self):
return "hello"
User.greet()This fails because self is missing.
Correct:
user = User()
user.greet()or:
User.greet(user)The method needs an instance.
Access through an instance binds the instance automatically.
Access through the class gives the raw function, so you must pass the instance yourself.
Example:
hasattr(obj, "name")This performs attribute lookup.
For simple objects, that is harmless.
But for objects with properties or custom attribute access, lookup can run code.
Example:
class Example:
@property
def value(self):
print("checking value")
return 10Then:
hasattr(Example(), "value")can print:
checking value
This is not a reason to avoid hasattr().
It is a reason to understand it.
It asks:
can this attribute be looked up without AttributeError?
not:
is there a simple key in __dict__?
Dynamic attribute tools:
getattr()
setattr()
delattr()are powerful.
But code like this can become hard to understand:
for key, value in data.items():
setattr(obj, key, value)Now object attributes depend on external data.
Maybe that is exactly right.
Maybe it is dangerous.
Ask:
Which attributes should this object have?
Are they known and documented?
Could unexpected keys overwrite important attributes?
Should this data stay in a dictionary instead?
Classes should make structure clearer.
Do not use dynamic attributes in a way that hides structure.
When designing attributes and methods, ask:
What state belongs to each instance?
What data should be shared by the class?
Which attributes are public?
Which attributes are internal?
Which behavior belongs as a method?
Should callers read this as an attribute or call it as a method?
Could a class attribute accidentally be mutated by all instances?
Does assignment mean per-instance override or shared class change?
Basic rules:
- Store per-object state on instances.
- Store truly shared defaults or constants on classes.
- Avoid mutable class attributes unless sharing is intentional.
- Use methods for behavior.
- Keep public attributes clear and stable.
- Use leading underscores for internal implementation details.
- Prefer explicit attributes over mysterious dynamic ones.
Good object design is mostly clear ownership of names and behavior.
- Create:
class User:
role = "member"
def __init__(self, name):
self.name = nameCreate a user.
Print:
vars(user)
vars(User)Which namespace contains name?
Which namespace contains role?
- Predict the output:
class User:
role = "member"
ada = User()
grace = User()
ada.role = "admin"
print(ada.role)
print(grace.role)
print(User.role)Explain using attribute lookup and assignment.
- Predict the output:
class Team:
members = []
red = Team()
blue = Team()
red.members.append("Ada")
print(blue.members)Why does this happen?
Rewrite the class so each team has its own members list.
- Create a class with a method:
class Greeter:
def greet(self):
return "hello"Inspect:
print(Greeter.greet)
print(Greeter().greet)What is the difference?
- Show that these are equivalent:
obj.method()
Class.method(obj)using a small class of your own.
- Use
getattr()to read an attribute whose name is stored in a variable:
field = "name"Then use setattr() to set another attribute dynamically.
When is this useful?
When might it be dangerous?
- Explain the difference between:
obj.attr = valueand:
obj.attr.append(value)Use a class attribute list example.
- Create an instance attribute that shadows a class attribute.
Then delete the instance attribute.
Show that the class attribute becomes visible again.
- Explain why this fails:
class User:
def greet(self):
return "hello"
User.greet()How can you call it correctly?
- In your own words, explain:
methods are attributes found through lookup
Use the terms:
class namespace
function object
bound method
self
In this chapter we learned:
- Attributes are names attached to objects.
- Dot syntax performs attribute access.
- Instance attributes live on individual instances.
- Class attributes live on class objects.
- Ordinary instances often store attributes in
__dict__. - Class objects have namespaces too.
- Attribute lookup can find names on instances and classes.
- Attribute assignment usually writes to the instance.
- Instance attributes can shadow class attributes.
- Deleting an instance attribute can reveal a class attribute.
- Mutating a found class attribute can affect all instances.
- Methods are functions stored on classes.
- Accessing a method through an instance creates a bound method.
- A bound method remembers the function and the instance.
obj.method()usually behaves likeClass.method(obj).getattr(),setattr(),hasattr(), anddelattr()provide dynamic attribute access tools.- Attribute lookup prepares us for inheritance, properties, descriptors, and MRO.
Core model:
obj.attr lookup:
look on object
look on class
look on parent classes
maybe invoke descriptor behavior
obj.attr = value:
usually assign on object
Method model:
class namespace:
method_name -> function object
instance lookup:
instance.method_name -> bound method
bound method:
function + instance
Attribute lookup is the engine under object-oriented Python.
Once you understand it, the advanced topics become far less mysterious.
Next we study encapsulation and managed attributes.
So far, attributes have mostly been direct:
user.name
user.email
account.balanceBut real programs often need rules around attributes.
Chapter 45 explains how Python handles that without abandoning its object model.
We will study:
- What encapsulation means in Python.
- Public and internal attributes.
- Leading underscore conventions.
- Name mangling with double underscores.
- Why Python does not use strict private fields by default.
- How properties manage attribute access.
- How validation can happen during assignment.
- Why methods and properties are API design tools.
- How managed attributes prepare us for descriptors.
The transition is direct:
attribute lookup explains how attributes are found
managed attributes explain how attribute access can be controlled
This is where Python's practical approach to encapsulation begins.