How to access (get or set) an object attribute in Python given the string that corresponds to the attribute’s name?

Object Attributes in Python

In Python, an object attribute is a type of variable that gets bound to the object. Attributes are employed in the storage of data-values or methods-which define the behavior and state of an object. The use of attributes encapsulates properties of an object that helps manage and organize related data and functions easily within a class.

What are Object Attributes in Python?

In Python, an object attribute is a characteristic of an object, which may hold values or represent functions. Object attributes are defined inside classes and, if defined, can only be accessed or modified after an object (instance of the class) has been created.

There are two types of attributes:

  • Instance Attributes: These are attributes of each object, or instance of the class. Each instance of a class can have different values of the same attribute.

Example:

class Car:
    def __init__(self, make, model):
        self.make = make  # Instance attribute
        self.model = model  # Instance attribute

car1 = Car("Toyota", "Camry")
car2 = Car("Honda", "Accord")

print(car1.make)  # Output: Toyota
print(car2.make)  # Output: Honda

  • Class Attributes: These are attributes shared among all instances of a class. They are defined at the class level and are the same for all objects of the class unless explicitly modified.

Example:

class Car:
    wheels = 4  # Class attribute

    def __init__(self, make, model):
        self.make = make
        self.model = model

car1 = Car("Toyota", "Camry")
car2 = Car("Honda", "Accord")

print(car1.wheels)  # Output: 4
print(car2.wheels)  # Output: 4

Instance versus Class Attributes Scope

Instance Attributes Class Attributes
Scope:Instance Attributes are specific to each instance (object) of a class. Changing an instance attribute for one object does not affect other objects.Class Attributes are shared across all instances of a class. Changing a class attribute affects all instances unless overridden.
Definition Location:Instance Attributes are typically defined within the __init__() method or elsewhere inside the class, where self is used to reference the specific object.Class Attributes are defined directly inside the class but outside any methods.
Access:Instance attributes are accessed and modified through individual objects.Class attributes can be accessed through both the class itself and the objects.

Example of Differences:

class Car:
    wheels = 4  # Class attribute

    def __init__(self, make, model):
        self.make = make  # Instance attribute
        self.model = model  # Instance attribute

car1 = Car("Toyota", "Camry")
car2 = Car("Honda", "Accord")

# Accessing instance attributes
print(car1.make)  # Output: Toyota
print(car2.make)  # Output: Honda

# Accessing class attributes
print(car1.wheels)  # Output: 4
print(car2.wheels)  # Output: 4

# Changing class attribute via the class
Car.wheels = 6
print(car1.wheels)  # Output: 6
print(car2.wheels)  # Output: 6

# Overriding class attribute in an instance
car1.wheels = 8
print(car1.wheels)  # Output: 8
print(car2.wheels)  # Output: 6

To sum up, instance attributes are solely related to the object, while class attributes are shared between objects of the class. How to gain mastery over the differentiation between them is quite central to handling state and behavior within a Python class effectively.

Dynamic Access of Attributes with getattr()

You can dynamically access object attributes using Python’s built-in function getattr(). This is useful when you don’t know in advance what attribute you want to access, and instead find out programmatically, for instance, when attribute names are bound as strings or are passed as arguments.

getattr() Explanation

getattr() in Python returns the value of a named attribute of an object. If the attribute exists, then getattr() will return its value. If not, it may either raise an error or return a default value, depending on whether a default has been provided or not.

Syntax:

getattr(object, attribute_name[, default])

  • object: The object from which you want to retrieve the attribute.
  • attribute_name: A string representing the name of the attribute you want to access.
  • default (optional): A default value to return if the specified attribute is not found.

Examples of Use getattr()

1.Basic Usage of getattr():

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

person = Person("John", 30)

# Accessing attributes dynamically
name_value = getattr(person, "name")
age_value = getattr(person, "age")

print(name_value)  # Output: John
print(age_value)   # Output: 30

In this example, getattr() is used to access the name and age attributes dynamically.

2.Using a Default Value When Attribute Doesn’t Exist

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

person = Person("John", 30)

# Accessing existing attribute
name_value = getattr(person, "name", "Unknown")
print(name_value)  # Output: John

# Accessing non-existing attribute with default value
height_value = getattr(person, "height", "Unknown")
print(height_value)  # Output: Unknown

Here, the getattr() function is used to attempt to access an attribute (height) that does not exist in the person object. Since a default value of “Unknown” is provided, it is returned instead of raising an error.

3.Handling Errors When the Attribute Is Missing

If you don’t provide a default value, and the attribute does not exist, getattr() will raise an AttributeError. You can handle this with a try-except block:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

person = Person("John", 30)

try:
    # Accessing a non-existing attribute without a default value
    height_value = getattr(person, "height")
    print(height_value)
except AttributeError as e:
    print(f"Error: {e}")  # Output: Error: 'Person' object has no attribute 'height'

In this example, because the height attribute does not exist and no default value is provided, getattr() raises an AttributeError, which is caught and handled in the except block.

Important Aspects

  • Dynamic Access: getattr() allows for flexible, dynamic attribute access when the attribute name is not known beforehand.
  • Default Value: If you want to avoid errors and make the code even more robust, you can tell it to return a default value when it doesn’t find the attribute.
  • Error Handling: The getattr() function raises an AttributeError by default if an attribute is missing, which can be handled along with the try-except block.

getattr() is one of Python’s most important tools in dynamic and introspective programming, used frequently in flexible data models (like JSON parsers, ORM frameworks, or in any case where the name of an attribute is determined in a runtime).

Setting Dynamic Attributes with setattr()

setattr() in Python is a built-in function that sets-or in other words, changes-the value of an attribute dynamically. This could be useful when the name of an attribute is not known until runtime or needed in order to change object attributes programmatically.

Explanation of setattr()

setattr() sets a value to an attribute of an object. If this attribute already exists, the function updates its value. If it does not exist, setattr() creates it dynamically and assigns the specified value.

Syntax

setattr(object, attribute_name, value)

  • object: The object for which you wish to set the attribute.
  • attribute_name: This is the string name of the attribute you’d like to set or change.
  • value: The value to be applied to the given attribute.

Example of setattr()

1.Setting Existing Attributes Dynamically

class Car:
    def __init__(self, make, model):
        self.make = make
        self.model = model

car = Car("Toyota", "Camry")

# Using setattr to modify an existing attribute
setattr(car, "model", "Corolla")

print(car.model)  # Output: Corolla

In this example, the setattr() function is used to change the model attribute of the car object from “Camry” to “Corolla”.

2.Setting New Attributes Dynamically

class Car:
    def __init__(self, make, model):
        self.make = make
        self.model = model

car = Car("Toyota", "Camry")

# Using setattr to create and set a new attribute 'year'
setattr(car, "year", 2022)

print(car.year)  # Output: 2022

Here, the setattr() function is used to add a new attribute year to the car object dynamically. The Car class initially had no year attribute, but setattr() allows it to be created and assigned a value.

Dynamically Setting New or Existing Attributes

setattr() can dynamically set both existing and new attributes. This makes setattr() very flexible, especially for situations in which attribute names and values could come from an external source, such as a configuration file or user input.

  • Modifying Existing Attributes If the attribute exists, setattr() simply changes its value.
  • Creating New Attributes If the attribute does not exist, setattr() creates the attribute dynamically.

Example of creating or updating attributes dynamically:

class User:
    def __init__(self, username):
        self.username = username

user = User("john_doe")

# Dynamically update an existing attribute
setattr(user, "username", "jane_doe")

# Dynamically create a new attribute
setattr(user, "email", "[email protected]")

print(user.username)  # Output: jane_doe
print(user.email)     # Output: [email protected]

How to Use setattr() with Type Checking

When dynamically setting attributes using setattr(), you might want to ensure that the value being set is of the right type. Python is dynamically typed but adding simple type checks can save you from unexpected behavior later on.

Example with Type Checking

You can also do a manual type check prior to the call to setattr() to make sure an attribute is set to the expected type. It may be done via an isinstance() or type() check.

class Car:
    def __init__(self, make, model):
        self.make = make
        self.model = model

car = Car("Toyota", "Camry")

# Type check before setting a new attribute 'year'
attribute_name = "year"
value = 2022

if isinstance(value, int):
    setattr(car, attribute_name, value)
else:
    raise TypeError(f"Expected an integer for attribute '{attribute_name}', got {type(value)}")

print(car.year)  # Output: 2022

In this example:

  • We ensure the value for the year attribute is of integer type using isinstance() function for type checking.
  • It raises a TypeError if the value does not match the type expected.

Accessing Private or Protected Attributes in Python

In Python, the naming convention treats an attribute of a class as public, protected, or private. This naming convention will affect how the attributes are accessed or modified, both inside and outside the class.

Explanation of Private (__) and Protected (_) Attributes

1.Public Attributes:

  • These are the attributes without any special prefix. They can be accessed and modified directly from outside the class.
  • Example:
class MyClass:
    def __init__(self):
        self.public_attr = "I am public"

obj = MyClass()
print(obj.public_attr)  # Output: I am public

2.Protected Attributes (_):

  • Conventionally, a single underscore followed by the attribute name denotes that it is protected. This is a weak form of encapsulation, meaning it shows not to access the attribute directly outside the class, but it can be.
  • Python does follow the convention but does not enforce it; hence, the attribute still can be accessed directly.
  • Example:
class MyClass:
    def __init__(self):
        self._protected_attr = "I am protected"

obj = MyClass()
print(obj._protected_attr)  # Output: I am protected

3.Private Attributes (__):

  • Attributes with a double underscore (__) before the name are considered private and cannot be accessed directly from outside the class. However, Python uses name mangling to allow indirect access to private attributes.
  • Example:
class MyClass:
    def __init__(self):
        self.__private_attr = "I am private"

obj = MyClass()
# print(obj.__private_attr)  # Raises AttributeError

Accessing and Modifying Private Attributes Using Name Mangling

Python does name mangling with private attributes to make them hard to access outside the class. It internally changes the name of private attributes with the pattern: _ClassName__attribute, where the ClassName is name of the class, and attribute is the original attribute name.

Example of Name Mangling

class MyClass:
    def __init__(self):
        self.__private_attr = "I am private"

obj = MyClass()

# Access private attribute using name mangling
print(obj._MyClass__private_attr)  # Output: I am private

In this example, Python internally changes the name of the __private_attr to _MyClass__private_attr, allowing access through the mangled name.

Modifying Private Attributes Using Name Mangling

class MyClass:
    def __init__(self):
        self.__private_attr = "I am private"

    def show(self):
        print(self.__private_attr)

obj = MyClass()
obj._MyClass__private_attr = "I am modified"  # Modify private attribute
obj.show()  # Output: I am modified

Here, you can modify the value of the private attribute by accessing it using its mangled name (_MyClass__private_attr).

Using getattr() and setattr() for Private Attributes

Since private attributes are mangled internally, you can still access and modify them dynamically using getattr() and setattr(), provided you use the mangled name.

Accessing Private Attributes with getattr()

class MyClass:
    def __init__(self):
        self.__private_attr = "I am private"

obj = MyClass()

# Access private attribute using getattr and name mangling
private_value = getattr(obj, "_MyClass__private_attr")
print(private_value)  # Output: I am private

Here, getattr() can retrieve the value of the private attribute by using its mangled name (_MyClass__private_attr).

Modifying Private Attributes with setattr()

class MyClass:
    def __init__(self):
        self.__private_attr = "I am private"

    def show(self):
        print(self.__private_attr)

obj = MyClass()

# Modify private attribute using setattr and name mangling
setattr(obj, "_MyClass__private_attr", "I am modified")
obj.show()  # Output: I am modified

In this example, setattr() is used to modify the private attribute by referencing its mangled name.

Practical Example: Accessing and Modifying Private Attributes Safely

You can encapsulate private attributes but still provide controlled access through public methods or functions like getattr() and setattr():

class BankAccount:
    def __init__(self, account_holder, balance):
        self.__account_holder = account_holder  # Private
        self.__balance = balance  # Private
    
    def show_balance(self):
        print(f"{self.__account_holder}'s balance is: ${self.__balance}")

account = BankAccount("Alice", 1000)

# Accessing private attribute using a public method
account.show_balance()  # Output: Alice's balance is: $1000

# Accessing private attributes using name mangling
account_holder = getattr(account, "_BankAccount__account_holder")
print(account_holder)  # Output: Alice

# Modifying private attribute using setattr and name mangling
setattr(account, "_BankAccount__balance", 1200)
account.show_balance()  # Output: Alice's balance is: $1200

Important Points

  • Protected attributes (_): That means the attribute is protected and is usually accessed internally within the class itself.
  • Private attributes (__): Refers to attributes that are not accessible directly from outside of a class. Name mangling happens to them. Access and/or modification of these attributes can happen by using the mangled names and/or methods like getattr() or setattr().
  • Name Mangling: Python changes private attribute names internally to _ClassName__attribute to prevent direct access from outside the class.
  • getattr() and setattr(): These functions allow for dynamic access and modification of private attributes by referencing their mangled names.

By using name mangling, in conjunction with dynamic access methods like getattr() and setattr(), you can still work with private attributes while abiding by Python’s principles of encapsulation.

Share The Tutorial With Your Friends
Twiter
Facebook
LinkedIn
Email
WhatsApp
Skype
Reddit

Check Our Ebook for This Online Course

Advanced topics are covered in this ebook with many practical examples.