본문 바로가기
DEV/파이썬 이론

파이썬 코딩 :: 파이썬 클래스, 함수, arg/kwarg, 패킹/언패킹, 객체지향, 유효성검사_TIL#12

by 올커 2022. 9. 16.

■ JITHub 개발일지(TIL : Today I Learned) 12일차


 

□  TIL :: 핵심내용

     파이썬 클래스, 함수, arg/kwarg, 패킹/언패킹, 객체지향, 유효성검사


1. 클래스

 - str 메서드(__str__) : init에서 규정한 클래스 자체 내용을 출력하고 싶을 때, 형식을 지정하는 메서드

   *인스턴스를 그냥 출력하면 주소값으로 나오는데, str을 지정해주면 지정된 형식의 값으로 출력됨

 

# 계산기 만들어보기(with class)
class Calc:
    # def __init__(self, a, b):
    #     self.a = a
    #     self.b = b

    def set_number(self, a, b):
        self.a = a
        self.b = b
        
    def plus(self):
        result = self.a + self.b
        return result

    def minus(self):
        result = self.a - self.b
        return result
    
    def multiple(self):
        result = self.a * self.b
        return result
    
    def divide(self):
        try:
            return self.a / self.b
        except ZeroDivisionError:
        	return "0으로는 나눌 수 없습니다."


# try except문 안에는 에러가 발생할 수 있는 코드만 들어가도록 하는 것이 좋다.
a = 0
b = 0
while True:
    try:
        a, b = [int(x) for x in input().split(" ")]
        break
    except ValueError:
    	print("숫자만 입력 가능합니다.")

calc = Calc()
calc.set_number(a, b)
print(calc.plus()) # 더한 값
print(calc.minus()) # 뺀 값
print(calc.multiple()) # 곱한 값
print(calc.divide()) # 나눈 값

 

2. 함수 심화

  - 함수에서 기본적으로 retrun을 만들지 않는다면, 또는 retrun만 있다면 결과값으로 None을 리턴해준다.

 

 

  - 타입 힌트

    : '이렇게 입력 받습니다'의 의미. 그러나 이것 자체로는 지키지않는다고 하여도, 에러를 발생시키지 않는다

  - 독스트링

 

 

  - 함수에는 정해진 변수를 지정하지 않으면 에러가 나지만, 기본값을 사용하도록 할 수도 있다.

 

 

  - 함수의 인자에 기본값 지정하기

# 함수를 선언할 때 인자에 기본값을 지정해줄 수 있습니다.
EXPRESSION = {
        0: lambda x, y: x + y ,
        1: lambda x, y: x - y ,
        2: lambda x, y: x * y ,
        3: lambda x, y: x / y
    }

def calc(num1, num2, option=None): # 인자로 option이 들어오지 않는 경우 기본값 할당
    """
    option
     - 0: 더하기
     - 1: 빼기
     - 2: 곱하기
     - 3: 나누기
    """
    return EXPRESSION[option](num1, num2) if option in EXPRESSION.keys() else False

print(calc(10, 20))    # False
print(calc(10, 20, 0)) # 30
print(calc(10, 20, 1)) # -10
print(calc(10, 20, 2)) # 200
print(calc(10, 20, 3)) # 0.5

 

  - arg/kwargs

   : args(arguments)와 keyword arguments(kwargs)는 함수에서 인자로 받을 값들의 갯수가 불규칙하거나 많을 때 주로 사용된다. 인자로 받을 값이 정해져있지 않기 때문에 함수를 더 동적으로 사용할 수 있으며, 함수를 선언할 때 args는 앞에 *를 붙여 명시하고, kwargs는 앞에 **를 붙여 명시한다.

   : arg는 튜플로 나오고, kwarg는 딕셔너리로 나온다.

 

   (1) args 활용 예시

def add(*args):
    # args = (1, 2, 3, 4)
    result = 0
    for i in args:
        result += i
        
    return result

print(add())           # 0
print(add(1, 2, 3))    # 6
print(add(1, 2, 3, 4)) # 10

   (2) kwargs 활용 예시

def set_profile(**kwargs):
    """
    kwargs = {
        name: "lee",
        gender: "man",
        age: 32,
        birthday: "01/01",
        email: "python@sparta.com"
    }
    """
    profile = {}
    profile["name"] = kwargs.get("name", "-")
    profile["gender"] = kwargs.get("gender", "-")
    profile["birthday"] = kwargs.get("birthday", "-")
    profile["age"] = kwargs.get("age", "-")
    profile["phone"] = kwargs.get("phone", "-")
    profile["email"] = kwargs.get("email", "-")
    
    return profile

profile = set_profile(
    name="lee",
    gender="man",
    age=32,
    birthday="01/01",
    email="python@sparta.com",
)

print(profile)
# result print
"""
{   
    'name': 'lee',
    'gender': 'man',
    'birthday': '01/01',
    'age': 32,
    'phone': '-',
    'email': 'python@sparta.com'
}
"""

 

   (3) kwargs 활용 예시 -2

class Profile():
    def set_profile(**kwargs):
        """
        kwargs = {
            name: "lee",
            gender: "man",
            age: 32,
            birthday: "01/01",
            email: "python@sparta.com"
        }
        """
        print(kwargs)
        profile["name"] = kwargs.get("name", "-")
        profile["gender"] = kwargs.get("gender", "-")
        profile["birthday"] = kwargs.get("birthday", "-")
        profile["age"] = kwargs.get("age", "-")
        profile["phone"] = kwargs.get("phone", "-")
        profile["email"] = kwargs.get("email", "-")

profile = Profile()
profile.set_profile(
    name="lee",
    gender="man",
    age=32,
    birthday="01/01",
    email="python@sparta.com",
)

 

   (4) kwargs 활용 예시 -3

        : key값이 입력되지 않으면 기본값, 있으면 값을 입력, 입력된 key가 딕셔너리에 없다면 무시한다.

class Profile():
    def set_profile(self, **kwargs):
        profile["name"] = kwargs.get("name", "-")
        profile["gender"] = kwargs.get("gender", "-")
        profile["birthday"] = kwargs.get("birthday", "-")
        profile["age"] = kwargs.get("age", "-")
        profile["phone"] = kwargs.get("phone", "-")
        profile["email"] = kwargs.get("email", "-")

profile = Profile()
profile.set_profile(
    name="lee",
    info="프로필입니다."
)

 

 

   (3) args, kwargs 혼용 활용 예시

def print_arguments(a, b, *args, **kwargs):
    print(a)
    print(b)
    print(args)
    print(kwargs)
    
print_arguments(
    1, # a
    2, # b
    3, 4, 5, 6, # args
    hello="world", keyword="argument" # kwargs
)

# result print
"""
1
2
(3, 4, 5, 6)
{'hello': 'hello', 'world': 'world'}
"""

 

  - .get

    : 딕셔너리에서 키 값을 찾아보고, 키 값이 없으면 기본값(콤마 뒤)를 반환)

test = {
    "key": "value"
}

print(test.get("k", "k는 없습니다."))

 

3. 패킹, 언패킹

  - 패킹(packing)/언패킹(unpacking)은 요소들을 묶어주거나 풀어주는 것을 의미한다.

  - 리스트 혹은 딕셔너리의 값을 합수에 입력할 때 주로 사용

  - 리스트에서의 활용(*) : 괄호'[]'를 풀어버린다.

def add(*args):
    result = 0
    for i in args:
        result += i
        
    return result

numbers = [1, 2, 3, 4]

print(add(*numbers)) # 10

"""아래 코드와 동일
print(add(1, 2, 3, 4))
"""

  - 딕셔너리에서의 활용(**) :괄호'{}'를 풀어버린다.

def set_profile(**kwargs):
    profile = {}
    profile["name"] = kwargs.get("name", "-")
    profile["gender"] = kwargs.get("gender", "-")
    profile["birthday"] = kwargs.get("birthday", "-")
    profile["age"] = kwargs.get("age", "-")
    profile["phone"] = kwargs.get("phone", "-")
    profile["email"] = kwargs.get("email", "-")
    
    return profile

user_profile = {
    "name": "lee",
    "gender": "man",
    "age": 32,
    "birthday": "01/01",
    "email": "python@sparta.com",
}

print(set_profile(**user_profile))
""" 아래 코드와 동일
profile = set_profile(
    name="lee",
    gender="man",
    age=32,
    birthday="01/01",
    email="python@sparta.com",
)
"""

# result print
"""
{
    'name': 'lee',
    'gender': 'man',
    'birthday': '01/01',
    'age': 32,
    'phone': '-',
    'email': 'python@sparta.com'
}

"""

 

4. 객체 지향(OOP : Object-Oriented Programming)

  - 객체지향이란 객체를 모델링하는 방향으로 코드를 작성하는 것을 의미한다.

  - 객체지향의 특성

    (1) 캡슐화(Encapsulation) : 특정 데이터의 액세스를 제한해 데이터가 직접적으로 수정되는 것을 방지하며, 검증 된 데이터만을 사용할 수 있다.

    (2) 추상화(Abstraction) : 사용되는 객체의 특성 중, 필요한 부분만 사용하고 필요하지 않은 부분은 제거하는 것을 의미한다.

    (3) 상속(Inheritance) : 상속은 기존에 작성 된 클래스의 내용을 수정하지 않고 그대로 사용하기 위해 사용되는 방식이며, 클래스를 선언할 때 상속받을 클래스를 지정할 수 있다.

    (4) 다형성(Polymorphism) : 하나의 객체가 다른 여러 객체로 재구성되는 것을 의미하며, 오버라이드, 오버로드가 다형성을 나타내는 대표적인 예시이다.

  - 객체지향의 장/단점

    (1) 장점

  • 클래스의 상속을 활용하기 때문에 코드의 재사용성이 높아진다.
  • 데이터를 검증하는 과정이 있기 때문에 신뢰도가 높다.
  • 모델링을 하기 수월하다.
  • 보안성이 높다.

    (2) 단점

  • 난이도가 높습니다.
  • 코드의 실행 속도가 비교적 느린 편입니다.
  • 객체의 역활과 기능을 정의하고 이해해야 하기 때문에 개발 속도가 느려집니다

  - 객체지향 예제

import re

# 숫자, 알파벳으로 시작하고 중간에 - 혹은 _가 포함될 수 있으며 숫자, 알파벳으로 끝나야 한다.
# @
# 알파벳, 숫자로 시작해야 하며 . 뒤에 2자의 알파벳이 와야 한다.
email_regex = re.compile(r'([A-Za-z0-9]+[.-_])*[A-Za-z0-9]+@[A-Za-z0-9-]+(\.[A-Z|a-z]{2,})+')

class Attendance:
    count = 0
        
    def attendance(self):
        self.count += 1
    
    @property
    def attendance_count(self):
        return self.count

class Profile(Attendance):
    def __init__(self, name, age, email):
        self.__name = name # __를 붙여주면 class 내부에서만 사용하겠다는 뜻
        self.__age = age
        self.__email = email
    
    @property # 읽기 전용 속성
    def name(self):
        return self.__name
    
    @name.setter # 쓰기 전용 속성
    def name(self, name):
        if isinstance(name, str) and len(name) >= 2:
            print("이름은 2자 이상 문자만 입력 가능합니다.")
            
        else:
            self.__name = name
    
    @property
    def age(self):
        return self.__age
    
    @age.setter
    def age(self, age):
        if isinstance(age, int):
            self.__age = age
            
        else:
            print("나이에는 숫자만 입력 가능합니다.")
        
    @property
    def email(self):
        return self.__email
    
    @email.setter
    def email(self, email):
        if re.fullmatch(email_regex, email):
            self.__email = email
        else:
            print("유효하지 않은 이메일입니다.")
        
    def attendance(self): # override
        super().attendance() # 부모 메소드 사용하기
        self.count += 1
    
    def __str__(self):
        return f"{self.__name} / {self.__age}"
    
    def __repr__(self):
        return f"{self.__name}"

 

5. 유효성 검사

  - __init__을 통해 딕셔너리 정의

  - isinstance를 통해 데이터 유효성을 관리

class Profile():
    def __init__(self, name, age, email):
        if not isinstance(age, int):
            print("나이는 숫자만 입력가능합니다")
            self.age = "-"
        self.name = name
        self.age = age
        self.email = email
        
profile = Profile("lee", "a", "python@gmail.com")

print(profile.name)
print(profile.age)
print(profile.email)

  - 클래스에서 이름 앞에 언더바가 하나 붙은 변수, 함수는 클래스 안에서만 사용하겠다라고 약속한다는 의미

    (클래스 밖에서 써도 문법적으로는 가능하지만, 안쓰는 것을 권장)

  - 클래스 안에서 선언한 변수, 함수도 밖에서 쓸 수 있다.

  - 언더바 두개를 붙이면 '맹글링' 으로 함수 외부에서도 강제적으로 사용하지 못하게 할 수 있다.

class Profile():
    def __init__(self, name, age, email):
        self.__name = name
        self.__age = age
        self.__email = email

  - setter, getter

# setter getter

class Profile():
    def __init__(self, name, age, email):
        self.__name = name
        self.__age = age
        self.__email = email
        
    def get_name(self):
        return self.__name
    
    def set_name(self, name):
        self.__name = name

  - 함수 내부에서만 사용할 수 있는 맹글링을 외부에서도 사용할 수 있도록 추가로 만들어주는 함수

 

# setter getter

class Profile():
    def __init__(self, name, age, email):
        self.__name = name
        self.__age = age
        self.__email = email
        
    def get_name(self):
        return self.__name
    
    def set_name(self, name):
        self.__name = name
        
    def age(self):
        return self.__age
        
    def set_age(self, age):
        if not isinstance(age, int):
            print("나이는 숫자만 입력가능합니다")
        else:
            self.__age = age
profile = Profile("lee", 30, "python@gmail.com")

profile.set_name("kim")
profile.set_age(20)
print(profile.get_name())
print(profile.get_age())
# print(profile.__age)
# print(profile.__email)

  - 외부에서 입력한 값에 대해 유효성을 판단할 수 있음.

 

  - property를 decorator로 설정하여 사용

    * decorator는 함수 이름 앞에 골뱅이@를 사용하면서 사용 가능.

    @property
    def get_name(self):
        return self.__name
    
    @name.setter
    def set_name(self, name):
        self.__name = name
    
    @property
    def age(self):
        return self.__age
    
    @age.setter
    def set_age(self, age):
        if not isinstance(age, int):
            print("나이는 숫자만 입력가능합니다")
        else:
            self.__age = age

원래대로 사용이 가능하도록 만들어주었다.

이메일 등과 같은 문자의 유효성 검사를 위해서는 regex 정규표현식 (regular expression)을 사용한다.

정규표현식을 사용하려면 re를 임포트하여 사용한다. import re

구글링을 통해 정규표현식을 가져와서 사용해도 된다.

 

  - random everything 사용하여 임의의 내용 입력하기

    : F1 키  →  random  →  원하는 입력값 선택

 

 

  - '__str__'과 '__repr__'의 차이

    클래스가 str으로 변경되서 호출될때 : __str__   →  print와 유사하다고 생각하면 된다.

    인스턴스 자체가 호출될 때 : __repr__

 

  - 클래스의 상속 : 클래스 선언시 괄호를 붙일 때 내용이 들어갈 수 있는

 

  - 오버라이딩

 

클래스의 코드는 함부로 바꿔서는 안된다.

그런데 나는 출석을 한번 할 때 2씩 올라가게 하고 싶다?

 -> 원본 클래스를 건들지 않고 똑같은 이름으로 새로 선언해주면 된다.

super().attendance()를 호출해서 사용하는 방법도 있다.

  기존의 함수를 동작시키고 여기에 +1을 더해주는 방법 ↓

 

 

 


■ TIT :: Today I thought

 - 오늘 익힌 내용은 생소한 개념이 많아 쉽지 않았다.

 - 클래스를 자유자재로 사용하고, 다양한 자료들에 익숙해지는 것이 필요하다. 

 - 데이터를 다루면서 조건이나 유효성 검사를 줘보면서 자주 실습하는 것이 좋겠다.

 

반응형

댓글