Overview

5 Hide class implementations

This chapter argues that robust Python designs hide class implementations to preserve loose coupling and make change safe. Encapsulation is achieved by exposing a minimal public interface while keeping state and helper behavior private (by convention, names starting with an underscore). This aligns with the Principle of Least Knowledge (Law of Demeter): classes should know as little as possible about each other and call only “nearby” objects, minimizing dependencies. The practical takeaway is “once public, always public”—default to privacy and reveal only what clients truly need.

To access hidden state safely, the chapter promotes properties as getters/setters that validate inputs and prevent callers from depending on representation. A detailed Date case study shows why: an initial, correct but inefficient, loop-based Gregorian implementation gives way to a far faster version based on Julian day numbers, and then to a hybrid with lazy evaluation that caches both y/m/d and Julian values behind synchronization flags. Because clients interact only through a stable public surface (constructor, getters, add_days, days_from), the class can be radically refactored without breaking callers. Lazy evaluation delays expensive conversions until needed, improving performance without leaking implementation details.

The chapter also warns that setters can be dangerous: per-field setters for year/month/day allow invalid states (for example, February 31), whereas validated bulk updates or immutable designs avoid such pitfalls. Even “immutable” objects can be compromised if getters expose internal mutable references; returning defensive copies (as with Employee.birthdate) preserves invariants. The Law of Demeter’s concrete rules, illustrated with the DemeterAuto example, discourage reaching through chains of objects. Finally, the Open-Closed Principle supports stability: keep common behavior closed to modification yet open to extension (for example, Mammal with Human and Cat subclasses), while keeping both shared and extended internals hidden. The net effect is code that is safer to evolve, easier to reason about, and harder to misuse.

A getter method enables code to obtain the price value from an Item object’s hidden state. A setter method enables code to modify the price value in an Item object’s hidden state. Neither method reveals how the class implements state. For example, we might later decide to have getter method price() dynamically look up the price from a database. We’ll be able to make this change without making changes to the method’s caller.
The version of class Date from iteration 1 performs date arithmetic correctly, but it does it poorly because of expensive looping day-by-day with the Gregorian calendar rules. This version deserves a lump of coal.
The version of class Date from iteration 2 performs data arithmetic very efficiently using Julian day numbers, but getter methods for the year, month, or day of the month must each call an expensive conversion algorithm. We get yet another lump of coal.
The version of class Date from iteration 3 is a hybrid that uses lazy evaluation. It has the best performance of the three versions. This version deserves the pot of gold.
A design of the book catalogue application with superclass Attributes and its subclasses. In the class diagram for Attributes, the name of private method _equal_ignore_case() is underlined to indicate that it is static.

Summary

  • Python programmers must respect the convention that names of instance variables and methods that begin with an underscore are denoted to be private and therefore have hidden implementations.
  • Minimize dependencies on a class by hiding how the class implements state to support encapsulation. Hide instance variables and methods by making denoting them to be private. When the hidden implementation of a class is encapsulated, we can refactor the implementation to improve it without causing changes to any code using the class.
  • Properties allow controlled access to hidden object state. A property can return values from an object’s state without revealing how the state is implemented. A property setter can modify an object’s state without revealing how the state is implemented.
  • A property should never allow an object to be put into an invalid state. We must be careful what property setters to provide.
  • An immutable class creates objects that cannot be modified after we’ve constructed them. Such a class should not provide property setters that modify an object’s state.
  • A class whose objects are supposed to be immutable must not provide references to hidden state implementation because that would allow other code to use the references to modify an object’s state at run time.
  • According to the Lazy Evaluation Principle, runtime performance should be improved by delaying an expensive calculation until its results are needed.
  • The Law of Demeter prescribes rules to guide designing classes that have no problematic dependencies on other classes. Method a() of class A can call method b() on a class B object if class A aggregates class B, or if the class B object was passed as an argument to method a(), or if method a() instantiated the class B object. Method a() should not call method b() if the class B object was returned by another object’s method.
  • The Open-Closed Principle says to close a superclass for modification but open it for extensions by subclasses. The superclass can hide and encapsulate the implementation of the common parts of the state and behavior inherited by its subclasses. This principle supports code stability.

FAQ

What does “hiding a class implementation” mean in Python, and why is it important?It means keeping a class’s internal state and helper methods inaccessible to clients, exposing only a minimal public interface. This reduces dependencies (loose coupling), lets you change internals without breaking users, and supports encapsulation. In Python, you do this by making implementation details “private” by convention.
How is privacy handled in Python if it isn’t enforced by the language?Python relies on conventions: prefix instance variables and helper methods with an underscore (e.g., _price, _calculate_pay). Other code should treat these as off-limits and subject to change. Because there’s no enforcement, teams must follow the convention to avoid brittle dependencies.
What is the Principle of Least Knowledge (Law of Demeter), and what are its practical rules?It says a class should know as little as possible about others. A method may call methods of: objects it owns (aggregates), objects passed as arguments, or objects it creates. It should not call methods on objects returned by other calls (avoid long navigation chains). This limits coupling and surprises.
When should I use property getters and setters, and how do they help encapsulation?Use @property to expose read access without revealing storage or computation, and use a corresponding setter only when mutation is needed and can be validated. Properties let you change how values are computed or stored later (e.g., a DB lookup) without changing call sites, so long as signatures remain stable.
Why can careless setter methods be dangerous, and how can I design safer mutations?Independent setters can put an object into an invalid state (e.g., setting month to 2 while day is 31). Prefer immutable designs when possible, validate changes, and if mutation is necessary, provide atomic setters that update related fields together (e.g., set_year_month_day) so the combination can be validated.
What is lazy evaluation, and how did the Date example use it?Lazy evaluation defers an expensive computation until it’s needed. The Date class kept both Gregorian (year, month, day) and a Julian day number, plus validity flags. It converted only when a value was requested or arithmetic was needed, avoiding repeated costly conversions.
How did switching to Julian day numbers improve date arithmetic performance?Date arithmetic becomes simple integer math: add n to move n days or subtract to get a difference. This avoids looping day-by-day through calendar rules. Conversions between Gregorian and Julian are done only when required, further optimized with lazy evaluation in the hybrid design.
If my object should be immutable, how do I prevent clients from mutating embedded objects?Don’t expose internal references. Copy mutable inputs in the constructor and return copies from getters. Otherwise, callers can retain or obtain a reference and mutate your hidden state (e.g., changing an Employee’s birthdate via a referenced Date).
What does “Once public, always public” imply for API design?Anything you make public becomes part of your contract and is hard to retract without breaking clients. Start with the smallest possible public surface—default to private, promote to public only when needed, and keep public signatures stable.
How does the Open-Closed Principle relate to hidden implementations and subclassing?It advises designing classes closed to modification but open to extension. Put stable, common behavior/state in a superclass (kept private/hidden internally), and extend via subclasses that add their own hidden implementation while adhering to the superclass’s public interface.

pro $24.99 per month

  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose one free eBook per month to keep
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime

lite $19.99 per month

  • access to all Manning books, including MEAPs!

team

5, 10 or 20 seats+ for your team - learn more


choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Software Design for Python Programmers ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Software Design for Python Programmers ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Software Design for Python Programmers ebook for free