Python 객체 속성 접근 및 수정 방법

PythonBeginner
지금 연습하기

소개

이 튜토리얼에서는 Python 객체의 속성에 접근하고 수정하는 방법을 살펴보겠습니다. 객체 속성을 다루는 방법을 이해하는 것은 Python 프로그래밍의 기본적인 기술입니다. 이 랩을 마치면 객체를 생성하고, 다양한 방법을 사용하여 속성에 접근하며, 객체 동작을 변경하기 위해 해당 속성을 수정할 수 있게 됩니다.

Python 은 객체 지향 프로그래밍 언어이며, 이는 Python 의 모든 것이 속성과 메서드를 가진 객체임을 의미합니다. 이러한 객체와 상호 작용하는 방법을 배우는 것은 효과적인 Python 애플리케이션을 구축하는 데 필수적입니다.

간단한 Python 클래스 생성하기

Python 클래스와 객체가 무엇인지, 그리고 어떻게 생성하는지 이해하는 것부터 시작해 보겠습니다.

클래스와 객체란 무엇인가요?

Python 에서 클래스는 객체를 생성하기 위한 청사진입니다. 객체는 클래스의 인스턴스이며, 다음을 포함합니다.

  • 속성 (Attributes): 데이터를 저장하는 변수
  • 메서드 (Methods): 객체가 수행할 수 있는 작업을 정의하는 함수

클래스를 템플릿으로, 객체를 해당 템플릿에서 생성된 것으로 생각하십시오.

첫 번째 클래스 생성하기

몇 가지 기본 속성을 가진 간단한 Person 클래스를 생성해 보겠습니다. 코드 편집기를 열고 /home/labex/project 디렉토리에 person.py라는 새 파일을 만듭니다.

## Define a Person class with name and age attributes
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def greet(self):
        return f"Hello, my name is {self.name} and I am {self.age} years old."

## Create a Person object
john = Person("John", 30)

## Print the object
print(john)
print(f"Type of john: {type(john)}")

이 코드는 두 개의 속성 (nameage) 과 하나의 메서드 (greet) 를 가진 Person 클래스를 정의합니다. 주요 구성 요소를 이해해 보겠습니다.

  • class Person: - 이 줄은 Person이라는 새 클래스를 선언합니다.
  • __init__ - 이것은 생성자 (constructor) 라고 하는 특수한 메서드로, 새 객체가 생성될 때 초기화합니다.
  • self - 이 매개변수는 생성되거나 조작되는 객체를 참조합니다.
  • self.name = name - 이것은 객체에 name이라는 속성을 생성하고 생성자에 전달된 값을 할당합니다.

이제 이 코드를 실행하여 어떤 일이 일어나는지 살펴보겠습니다.

python3 /home/labex/project/person.py

다음과 유사한 출력을 볼 수 있습니다.

<__main__.Person object at 0x7f8b2c3d9d90>
Type of john: <class '__main__.Person'>

이 출력은 johnPerson 클래스의 객체임을 알려줍니다. 16 진수 숫자는 객체가 저장된 메모리 주소입니다.

여러 객체 생성하기

단일 클래스에서 여러 객체를 생성할 수 있습니다. person.py 파일을 수정하여 다른 Person 객체를 생성해 보겠습니다.

## Define a Person class with name and age attributes
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def greet(self):
        return f"Hello, my name is {self.name} and I am {self.age} years old."

## Create two Person objects
john = Person("John", 30)
alice = Person("Alice", 25)

## Print information about both objects
print(f"First person: {john.name}, {john.age}")
print(f"Second person: {alice.name}, {alice.age}")

업데이트된 코드를 실행합니다.

python3 /home/labex/project/person.py

다음과 같은 출력을 볼 수 있습니다.

First person: John, 30
Second person: Alice, 25

이제 각자 고유한 nameage 속성을 가진 두 개의 서로 다른 Person 객체를 생성했습니다.

객체 속성 접근하기

이제 속성이 있는 객체를 생성했으므로 이러한 속성에 접근하는 다양한 방법을 알아보겠습니다.

점 표기법 사용하기

객체 속성에 접근하는 가장 일반적인 방법은 점 표기법 (object.attribute) 을 사용하는 것입니다.

/home/labex/project 디렉토리에 access_attributes.py라는 새 파일을 생성해 보겠습니다.

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

    def greet(self):
        return f"Hello, my name is {self.name} and I am {self.age} years old."

## Create a Person object
john = Person("John", 30, "Developer")

## Access attributes using dot notation
print(f"Name: {john.name}")
print(f"Age: {john.age}")
print(f"Job: {john.job}")

## Access and call a method
greeting = john.greet()
print(f"Greeting: {greeting}")

이 코드를 실행합니다.

python3 /home/labex/project/access_attributes.py

다음과 같은 출력을 볼 수 있습니다.

Name: John
Age: 30
Job: Developer
Greeting: Hello, my name is John and I am 30 years old.

변수를 사용하여 속성 접근하기

때로는 속성 이름을 문자열로만 가지고 있을 때 속성에 접근해야 할 수 있습니다. 이는 동적으로 결정된 속성 이름으로 작업할 때 유용할 수 있습니다.

access_attributes.py 파일을 수정해 보겠습니다.

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

    def greet(self):
        return f"Hello, my name is {self.name} and I am {self.age} years old."

## Create a Person object
john = Person("John", 30, "Developer")

## List of attributes to access
attributes = ["name", "age", "job"]

## Access attributes using a loop and dot notation
print("Accessing attributes using dot notation:")
for attr in attributes:
    if attr == "name":
        print(f"Name: {john.name}")
    elif attr == "age":
        print(f"Age: {john.age}")
    elif attr == "job":
        print(f"Job: {john.job}")

## Call a method
greeting = john.greet()
print(f"Greeting: {greeting}")

코드를 실행합니다.

python3 /home/labex/project/access_attributes.py

다음과 같은 출력을 볼 수 있습니다.

Accessing attributes using dot notation:
Name: John
Age: 30
Job: Developer
Greeting: Hello, my name is John and I am 30 years old.

이것은 약간 번거롭습니다. 다음 단계에서는 속성에 동적으로 접근하는 더 우아한 방법을 배우겠습니다.

getattr()setattr() 함수 사용하기

Python 은 객체 속성에 동적으로 접근하고 수정할 수 있는 내장 함수를 제공합니다. 이러한 함수는 런타임까지 알 수 없는 속성 이름으로 작업하려는 경우 특히 유용합니다.

getattr() 함수

getattr() 함수를 사용하면 속성 이름을 문자열로 사용하여 속성에 접근할 수 있습니다. 구문은 다음과 같습니다.

getattr(object, attribute_name, default_value)
  • object: 속성에 접근하려는 객체
  • attribute_name: 속성 이름을 포함하는 문자열
  • default_value: 속성이 존재하지 않는 경우 반환할 선택적 값

/home/labex/project 디렉토리에 dynamic_attributes.py라는 새 파일을 생성해 보겠습니다.

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

    def greet(self):
        return f"Hello, my name is {self.name} and I am {self.age} years old."

## Create a Person object
john = Person("John", 30, "Developer")

## List of attributes to access
attributes = ["name", "age", "job", "address"]

## Access attributes using getattr()
print("Accessing attributes using getattr():")
for attr in attributes:
    ## The third parameter is the default value if the attribute doesn't exist
    value = getattr(john, attr, "Not available")
    print(f"{attr.capitalize()}: {value}")

## Call a method using getattr
greet_method = getattr(john, "greet")
greeting = greet_method()
print(f"Greeting: {greeting}")

이 코드를 실행합니다.

python3 /home/labex/project/dynamic_attributes.py

다음과 같은 출력을 볼 수 있습니다.

Accessing attributes using getattr():
Name: John
Age: 30
Job: Developer
Address: Not available
Greeting: Hello, my name is John and I am 30 years old.

존재하지 않는 address 속성의 경우, getattr()은 오류를 발생시키는 대신 기본값 "Not available"을 반환합니다.

setattr() 함수

setattr() 함수를 사용하면 속성 이름을 문자열로 사용하여 속성을 설정할 수 있습니다. 구문은 다음과 같습니다.

setattr(object, attribute_name, value)
  • object: 속성을 설정하려는 객체
  • attribute_name: 속성 이름을 포함하는 문자열
  • value: 속성에 할당할 값

setattr() 사용 예제를 포함하도록 dynamic_attributes.py 파일을 수정해 보겠습니다.

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

    def greet(self):
        return f"Hello, my name is {self.name} and I am {self.age} years old."

## Create a Person object
john = Person("John", 30, "Developer")

print("Original attributes:")
print(f"Name: {john.name}")
print(f"Age: {john.age}")
print(f"Job: {john.job}")

## Modify attributes using setattr()
print("\nModifying attributes using setattr():")
setattr(john, "name", "John Smith")
setattr(john, "age", 31)
setattr(john, "job", "Senior Developer")

## Add a new attribute using setattr()
setattr(john, "address", "123 Main St")

## Print the modified attributes
print("\nModified attributes:")
print(f"Name: {john.name}")
print(f"Age: {john.age}")
print(f"Job: {john.job}")
print(f"Address: {john.address}")

## Access attributes using getattr()
print("\nAccessing attributes using getattr():")
for attr in ["name", "age", "job", "address"]:
    value = getattr(john, attr, "Not available")
    print(f"{attr.capitalize()}: {value}")

업데이트된 코드를 실행합니다.

python3 /home/labex/project/dynamic_attributes.py

다음과 같은 출력을 볼 수 있습니다.

Original attributes:
Name: John
Age: 30
Job: Developer

Modifying attributes using setattr():

Modified attributes:
Name: John Smith
Age: 31
Job: Senior Developer
Address: 123 Main St

Accessing attributes using getattr():
Name: John Smith
Age: 31
Job: Senior Developer
Address: 123 Main St

기존 속성을 수정하고 클래스에 정의되지 않은 완전히 새로운 속성 (address) 을 추가할 수 있었습니다.

실용적인 예시

동적 속성 접근 및 수정이 유용한 더 실용적인 예시를 만들어 보겠습니다. /home/labex/project 디렉토리에 data_processor.py라는 파일을 생성합니다.

class DataRecord:
    def __init__(self):
        ## Start with no attributes
        pass

## Create a function to process a dictionary into an object
def dict_to_object(data_dict):
    record = DataRecord()
    for key, value in data_dict.items():
        setattr(record, key, value)
    return record

## Sample data (could come from a database, API, etc.)
user_data = {
    "user_id": 12345,
    "username": "johndoe",
    "email": "john@example.com",
    "active": True,
    "login_count": 27
}

## Convert the dictionary to an object
user = dict_to_object(user_data)

## Access the attributes
print(f"User ID: {user.user_id}")
print(f"Username: {user.username}")
print(f"Email: {user.email}")
print(f"Active: {user.active}")
print(f"Login Count: {user.login_count}")

## We can also add or modify attributes
setattr(user, "last_login", "2023-09-15")
setattr(user, "login_count", 28)

print("\nAfter modifications:")
print(f"Login Count: {user.login_count}")
print(f"Last Login: {user.last_login}")

## We can also access all attributes dynamically
print("\nAll attributes:")
for attr in ["user_id", "username", "email", "active", "login_count", "last_login"]:
    value = getattr(user, attr, None)
    print(f"{attr}: {value}")

이 코드를 실행합니다.

python3 /home/labex/project/data_processor.py

다음과 같은 출력을 볼 수 있습니다.

User ID: 12345
Username: johndoe
Email: john@example.com
Active: True
Login Count: 27

After modifications:
Login Count: 28
Last Login: 2023-09-15

All attributes:
user_id: 12345
username: johndoe
email: john@example.com
active: True
login_count: 28
last_login: 2023-09-15

이 예제는 데이터베이스, API 또는 파일에서 가져올 수 있는 딕셔너리에서 데이터를 객체로 변환한 다음 해당 속성에 동적으로 접근하거나 수정하는 방법을 보여줍니다.

객체 속성의 실용적인 응용

이제 객체 속성에 접근하고 수정하는 방법을 이해했으므로 이러한 개념의 실용적인 응용을 살펴보겠습니다. 실제 응용 프로그램에서 객체 속성을 사용하는 방법을 보여주기 위해 간단한 재고 관리 시스템을 만들 것입니다.

재고 시스템 만들기

/home/labex/project 디렉토리에 inventory.py라는 파일을 생성해 보겠습니다.

class Product:
    def __init__(self, product_id, name, price, quantity):
        self.product_id = product_id
        self.name = name
        self.price = price
        self.quantity = quantity

    def total_value(self):
        return self.price * self.quantity

    def display_info(self):
        return f"Product: {self.name} (ID: {self.product_id})\nPrice: ${self.price:.2f}\nQuantity: {self.quantity}\nTotal Value: ${self.total_value():.2f}"


class Inventory:
    def __init__(self):
        self.products = {}

    def add_product(self, product):
        self.products[product.product_id] = product
        print(f"Added: {product.name}")

    def remove_product(self, product_id):
        if product_id in self.products:
            product = self.products.pop(product_id)
            print(f"Removed: {product.name}")
            return True
        print(f"Product with ID {product_id} not found.")
        return False

    def update_quantity(self, product_id, new_quantity):
        if product_id in self.products:
            product = self.products[product_id]
            old_quantity = product.quantity
            product.quantity = new_quantity
            print(f"Updated quantity of {product.name} from {old_quantity} to {new_quantity}")
            return True
        print(f"Product with ID {product_id} not found.")
        return False

    def display_inventory(self):
        if not self.products:
            print("Inventory is empty.")
            return

        print("\n===== Current Inventory =====")
        total_value = 0
        for product in self.products.values():
            print(f"\n{product.display_info()}")
            total_value += product.total_value()

        print(f"\nTotal Inventory Value: ${total_value:.2f}")
        print("============================")


## Create an inventory
inventory = Inventory()

## Create some products
laptop = Product(1, "Laptop", 999.99, 5)
phone = Product(2, "Smartphone", 499.95, 10)
headphones = Product(3, "Wireless Headphones", 149.99, 15)

## Add products to inventory
inventory.add_product(laptop)
inventory.add_product(phone)
inventory.add_product(headphones)

## Display the inventory
inventory.display_inventory()

## Update quantities
inventory.update_quantity(1, 7)  ## Increase laptop quantity
inventory.update_quantity(3, 10)  ## Decrease headphones quantity

## Display the updated inventory
inventory.display_inventory()

## Remove a product
inventory.remove_product(2)  ## Remove smartphones

## Display the final inventory
inventory.display_inventory()

이 코드를 실행합니다.

python3 /home/labex/project/inventory.py

다음과 유사한 출력을 볼 수 있습니다.

Added: Laptop
Added: Smartphone
Added: Wireless Headphones

===== Current Inventory =====

Product: Laptop (ID: 1)
Price: $999.99
Quantity: 5
Total Value: $4999.95

Product: Smartphone (ID: 2)
Price: $499.95
Quantity: 10
Total Value: $4999.50

Product: Wireless Headphones (ID: 3)
Price: $149.99
Quantity: 15
Total Value: $2249.85

Total Inventory Value: $12249.30
============================
Updated quantity of Laptop from 5 to 7
Updated quantity of Wireless Headphones from 15 to 10

===== Current Inventory =====

Product: Laptop (ID: 1)
Price: $999.99
Quantity: 7
Total Value: $6999.93

Product: Smartphone (ID: 2)
Price: $499.95
Quantity: 10
Total Value: $4999.50

Product: Wireless Headphones (ID: 3)
Price: $149.99
Quantity: 10
Total Value: $1499.90

Total Inventory Value: $13499.33
============================
Removed: Smartphone

===== Current Inventory =====

Product: Laptop (ID: 1)
Price: $999.99
Quantity: 7
Total Value: $6999.93

Product: Wireless Headphones (ID: 3)
Price: $149.99
Quantity: 10
Total Value: $1499.90

Total Inventory Value: $8499.83
============================

동적 속성으로 작업하기

getattr()setattr()을 사용하여 동적 속성을 처리하도록 재고 시스템을 개선해 보겠습니다. /home/labex/project 디렉토리에 enhanced_inventory.py라는 파일을 생성합니다.

class Product:
    def __init__(self, product_id, name, price, quantity, **kwargs):
        self.product_id = product_id
        self.name = name
        self.price = price
        self.quantity = quantity

        ## Add any additional attributes provided
        for key, value in kwargs.items():
            setattr(self, key, value)

    def total_value(self):
        return self.price * self.quantity

    def display_info(self):
        result = [f"Product: {self.name} (ID: {self.product_id})",
                  f"Price: ${self.price:.2f}",
                  f"Quantity: {self.quantity}",
                  f"Total Value: ${self.total_value():.2f}"]

        ## Display additional attributes
        standard_attrs = {'product_id', 'name', 'price', 'quantity'}
        for attr in dir(self):
            if not attr.startswith('__') and not callable(getattr(self, attr)) and attr not in standard_attrs:
                value = getattr(self, attr)
                result.append(f"{attr.replace('_', ' ').title()}: {value}")

        return '\n'.join(result)


class Inventory:
    def __init__(self):
        self.products = {}

    def add_product(self, product):
        self.products[product.product_id] = product
        print(f"Added: {product.name}")

    def update_attribute(self, product_id, attribute, value):
        if product_id in self.products:
            product = self.products[product_id]
            old_value = getattr(product, attribute, None)
            setattr(product, attribute, value)
            print(f"Updated {attribute} of {product.name} from {old_value} to {value}")
            return True
        print(f"Product with ID {product_id} not found.")
        return False

    def display_inventory(self):
        if not self.products:
            print("Inventory is empty.")
            return

        print("\n===== Current Inventory =====")
        total_value = 0
        for product in self.products.values():
            print(f"\n{product.display_info()}")
            total_value += product.total_value()

        print(f"\nTotal Inventory Value: ${total_value:.2f}")
        print("============================")


## Create an inventory
inventory = Inventory()

## Create products with additional attributes
laptop = Product(1, "Laptop", 999.99, 5, brand="TechPro", model="X5", color="Silver", warranty_years=2)
phone = Product(2, "Smartphone", 499.95, 10, brand="MobiCom", model="Galaxy", color="Black", has_5g=True)
headphones = Product(3, "Wireless Headphones", 149.99, 15, brand="AudioMax", battery_life_hours=20, is_noise_cancelling=True)

## Add products to inventory
inventory.add_product(laptop)
inventory.add_product(phone)
inventory.add_product(headphones)

## Display the inventory
inventory.display_inventory()

## Update a standard attribute
inventory.update_attribute(1, "price", 1099.99)

## Update a custom attribute
inventory.update_attribute(3, "battery_life_hours", 25)

## Add a new attribute to an existing product
inventory.update_attribute(2, "water_resistant", True)

## Display the updated inventory
inventory.display_inventory()

이 코드를 실행합니다.

python3 /home/labex/project/enhanced_inventory.py

다음과 유사한 출력을 볼 수 있습니다.

Added: Laptop
Added: Smartphone
Added: Wireless Headphones

===== Current Inventory =====

Product: Laptop (ID: 1)
Price: $999.99
Quantity: 5
Total Value: $4999.95
Brand: TechPro
Color: Silver
Model: X5
Warranty Years: 2

Product: Smartphone (ID: 2)
Price: $499.95
Quantity: 10
Total Value: $4999.50
Brand: MobiCom
Color: Black
Has 5g: True
Model: Galaxy

Product: Wireless Headphones (ID: 3)
Price: $149.99
Quantity: 15
Total Value: $2249.85
Battery Life Hours: 20
Brand: AudioMax
Is Noise Cancelling: True

Total Inventory Value: $12249.30
============================
Updated price of Laptop from 999.99 to 1099.99
Updated battery_life_hours of Wireless Headphones from 20 to 25
Updated water_resistant of Smartphone from None to True

===== Current Inventory =====

Product: Laptop (ID: 1)
Price: $1099.99
Quantity: 5
Total Value: $5499.95
Brand: TechPro
Color: Silver
Model: X5
Warranty Years: 2

Product: Smartphone (ID: 2)
Price: $499.95
Quantity: 10
Total Value: $4999.50
Brand: MobiCom
Color: Black
Has 5g: True
Model: Galaxy
Water Resistant: True

Product: Wireless Headphones (ID: 3)
Price: $149.99
Quantity: 15
Total Value: $2249.85
Battery Life Hours: 25
Brand: AudioMax
Is Noise Cancelling: True

Total Inventory Value: $12749.30
============================

이 향상된 재고 시스템에서는 getattr()setattr()을 보다 복잡한 응용 프로그램에서 사용하는 방법을 보여주었습니다.

  1. Product 클래스는 **kwargs를 통해 추가 속성을 허용하고 setattr()을 사용하여 동적으로 설정합니다.
  2. display_info() 메서드는 getattr()을 사용하여 제품의 모든 속성을 표시합니다.
  3. Inventory 클래스의 update_attribute() 메서드는 getattr()을 사용하여 현재 값을 검색하고 setattr()을 사용하여 업데이트합니다.

이 접근 방식은 매우 유연하며 클래스 정의를 수정하지 않고도 다양한 속성 집합을 가진 제품을 처리할 수 있습니다.

요약

이 랩에서는 효과적인 Python 프로그래밍을 위한 기본적인 기술인 Python 객체 속성으로 작업하는 방법을 배웠습니다. 다음은 수행한 작업에 대한 요약입니다.

  1. 클래스 및 객체 생성: 속성과 메서드가 있는 Python 클래스를 생성하고 이러한 클래스에서 객체를 인스턴스화하는 방법을 배웠습니다.

  2. 객체 속성 접근: 다음을 포함하여 객체 속성에 접근하는 다양한 방법을 살펴보았습니다.

    • 점 표기법 사용 (object.attribute)
    • 동적 접근을 위한 getattr() 함수 사용
  3. 객체 속성 수정: 다음을 사용하여 객체 속성을 변경하는 방법을 배웠습니다.

    • 점 표기법을 사용한 직접 할당 (object.attribute = value)
    • 동적 수정을 위한 setattr() 함수
  4. 실용적인 응용: 이러한 개념을 실용적인 응용 프로그램에 적용했습니다.

    • 간단한 재고 관리 시스템
    • 동적 속성 처리가 가능한 향상된 시스템

이러한 기술은 보다 유연하고 강력한 Python 응용 프로그램을 구축하는 데 도움이 될 것입니다. 객체 속성으로 작업하는 방법을 이해하는 것은 데이터 처리, 구성 관리 및 확장 가능한 소프트웨어 시스템 생성과 같은 작업에 필수적입니다.

추가 연습을 위해 다음과 같은 추가 기능으로 재고 관리 시스템을 확장하는 것을 고려해 보십시오.

  • 속성을 기반으로 제품 필터링
  • 다양한 기준에 따라 제품 정렬
  • 속성 수정에 대한 유효성 검사 추가
  • 재고 데이터를 저장하고 로드하기 위한 직렬화 구현