본문 바로가기

PYTHON-BACK

#파이썬 기초 8일차_1

728x90

1.2 객체의 특징

  • object 클래스로 객체의 레퍼런스 확인하기 (인스턴스 객체를 만들어서 사용)
a = object()
b = object()

print(a == b)
print(a is b)
print(id(a), id(b))

False

False

133982244159440 133982244159424

 

t와 ts는 같은 변수인것을 알 수 있다.

t = (1,2,3)
ts = tuple(t)

print(t == ts)
print(t is ts)
print(id(t), id(ts))

True
True
133982029611968 133982029611968

  • int 클래스에 대한 책임성 확인하기
  • 대부분의 종속성은 앞에 것이 주최가 되서 나머지가 종속된다.
a = int(10)

b = int(5)
 
print(a+b) # 우리 눈으로는 a와b를 동등하게 해서 둘을 더해라
print(a.__add__(b)) # a에다 b를 더해서 가지고와라
print(a-benumerate
print(a.__sub__(b))

15

15

5

5

  • int 클래스 내에서 관리하는 속성과 메소드 확인하기
  • enumerate는 데이터 전송시 인덱스를 같이 붙여서 보내주는것
for i,v in enumerate(dir(int),1) : # 0번을 1번으로 바꿔서 순서대로 가지고 오는것
    print(v,end=" ") # 0번을 1번으로 바꿔서 순서대로 가지고 오는것
    if i % 5 == 0 :
        print()

__abs__ __add__ __and__ __bool__ __ceil__ 
__class__ __delattr__ __dir__ __divmod__ __doc__ 
__eq__ __float__ __floor__ __floordiv__ __format__ 
__ge__ __getattribute__ __getnewargs__ __gt__ __hash__ 
__index__ __init__ __init_subclass__ __int__ __invert__ 
__le__ __lshift__ __lt__ __mod__ __mul__ 
__ne__ __neg__ __new__ __or__ __pos__ 
__pow__ __radd__ __rand__ __rdivmod__ __reduce__ 
__reduce_ex__ __repr__ __rfloordiv__ __rlshift__ __rmod__ 
__rmul__ __ror__ __round__ __rpow__ __rrshift__ 
__rshift__ __rsub__ __rtruediv__ __rxor__ __setattr__ 
__sizeof__ __str__ __sub__ __subclasshook__ __truediv__ 
__trunc__ __xor__ as_integer_ratio bit_count bit_length 
conjugate denominator from_bytes imag numerator 
real to_bytes

1.3 최상위 클래스 object 이해하기

  • object 클래스 내부의 속성과 메소드 보기
  • 기본 오브젝트 클래스는 기본이 되는 클래스들만 가지고 있다.
for i,v in enumerate(dir(object),1) :
    print(v,end=" ")
    if i % 6 == 0 :
        print()

__class__ __delattr__ __dir__ __doc__ __eq__ __format__ 
__ge__ __getattribute__ __gt__ __hash__ __init__ __init_subclass__ 
__le__ __lt__ __ne__ __new__ __reduce__ __reduce_ex__ 
__repr__ __setattr__ __sizeof__ __str__ __subclasshook__ 

 

아래와 같이 사용하면 문서로 해당 내용 설명해줌

print(object.__doc__)

The base class of the class hierarchy.

When called, it accepts no arguments and returns a new featureless
instance that has no instance attributes and cannot be given any.

print(object.__name__)
print(object.__str__(object))
print(object.__repr__(object)) # 자기정보 표현

object
<class 'object'>
<type object at 0x584ba43bb800>

 

모든 객체의 기본적인 동등성 비교 메서드로, 이 메서드는 두 객체가 동일한 값을 가지는지 여부를 확인

print(object.__eq__(object, object))
print(object is object)

True

True

 

1.4 파이썬 클래스 생성 및 기본 상속 구조

 

1.4.1 클래스의 생성과 상속

 

 

  • Type 클래스의 instance 여부 확인
  • Type 클래스를 기반으로 인스턴스 만들어진 것들이다.
  • 인스턴스는 객체 지향 프로그래밍(OOP)에서 해당 클래스의 구조로 컴퓨터 저장 공간에서 할당된 실체를 의미
l = [type, object, int, float, str, tuple, list, dict,set]

for i in l :
    print(isinstance(i,type))

True
True
True
True
True
True
True
True
True

 

  • 서브클래스는 베이스가 오브젝트이다.
l = [type, object, int, float, str, tuple, list, dict,set]

for i in l :
    print(issubclass(i,object))

True
True
True
True
True
True
True
True
True

  • Class 내부 속성 __ class __, __ bases __ 확인
l = ['type', 'object', 'int', 'float', 'str', 'tuple', 'list', 'dict','set']

for i in l :
    print(eval(i+".__class__"))

<class 'type'>
<class 'type'>
<class 'type'>
<class 'type'>
<class 'type'>
<class 'type'>
<class 'type'>
<class 'type'>
<class 'type'>

l = ['type', 'object', 'int', 'float', 'str', 'tuple', 'list', 'dict','set']

for i in l :
    print(eval(i+".__bases__"))

(<class 'object'>,)
()
(<class 'object'>,)
(<class 'object'>,)
(<class 'object'>,)
(<class 'object'>,)
(<class 'object'>,)
(<class 'object'>,)
(<class 'object'>,)

1.4.2 클래스와 인스턴스의 관계

 

메타 클래스를 통해서 만들어지게 된다. (메타 데이터를 관리하는 클래스)

  • 내장 클래스와 인스턴스의 관계 확인
l = ['object', 'int', 'float', 'str', 'tuple', 'list', 'dict','set']

for i in l :
    print(eval("isinstance("+i+"(),"+i+")"))

True
True
True
True
True
True
True
True

 

isinstance() 함수는 지정된 객체가 지정된 유형인지 확인

만약 유형 매개변수가 튜플이라면, 해당 객체가 튜플 내의 유형 중 하나인 경우 True를 반환

l = ['object', 'int', 'float', 'str', 'tuple', 'list', 'dict','set']

for i in l :
    print(eval(i+"().__class__"))

<class 'object'>
<class 'int'>
<class 'float'>
<class 'str'>
<class 'tuple'>
<class 'list'>
<class 'dict'>
<class 'set'>

 

1.5 사용자 정의 클래스

  • 사용자 정의 클래스 기본 확인
  • 기존의 클래스를 베이스로 학습
class Klass :
    pass

print(Klass.__class__)
print(Klass.__bases__)

print(isinstance(Klass, type)) # 타입의 인스턴스가 맞나?
print(issubclass(Klass, object)) # 오브젝트의 서브클래스가 맞나?

<class 'type'>
(<class 'object'>,)
True
True

import pprint

class Int(int) :
    pass

a = Int(10)
print(type(a),a)

pprint.pprint(Int.__dict__)

<class '__main__.Int'> 10
mappingproxy({'__dict__': <attribute '__dict__' of 'Int' objects>,
              '__doc__': None,
              '__module__': '__main__'})

print(Int.__class__)
print(Int.__bases__)

<class 'type'>
(<class 'int'>,)

print(isinstance(Int, type))
print(issubclass(Int, object))
print(issubclass(Int, int))

True
True
True

  • Person 사용자 클래스를 정의 및 속성 확인
class Person :
    def __init__(self, name, age) : # 초기화함수
        self.name = name
        self.age  = age
p = Person("줄리아",15)

print(p)
print(p.__dict__)
print(p.name)
print(p.age)

<__main__.Person object at 0x79db0214f2b0>
{'name': '줄리아', 'age': 15}
줄리아
15

 

오브젝트가 가지는 메소드 , Person가 가지는 메소드의 차집합으로 Person에서만 가지는 메소드만 확인

o = set(dir(object))
pc = set(dir(Person))

print(pc-o)

{'__module__', '__dict__', '__weakref__'}

print(Person.__module__)
print(Person.__bases__)
print(Person.__class__)

__main__
(<class 'object'>,)
<class 'type'>

1.6 객체 네임스페이스 및 스코프

1.6.1 인스턴스와 클래스 객체 네임스페이스 및 스코프 처리 기준

 

자기를 생성한 네임스페이스를 볼 수 있는 권한이 있어 거꾸로 올라가면서 볼 수 있다.

 

  • 클래스와 인스턴스 객체간의 네임스페이스 접근
class Klass :
    name = "Klass attr"
    def __init__(self, name) :
        self.name = name

    def getname(self) :
        return self.name
k = Klass("instance attr")

print(k.name) # 바뀐 이후 출력
print(Klass.name) # 원상태 그대로 출력

instance attr

Klass attr

 

다른 언어에서는 안됨

k.getclassname = Klass.name
print(k.__dict__)
print(k.getclassname)

{'name': 'instance attr', 'getclassname': 'Klass attr'}

Klass attr

 

  • 사용자 데이터를 활용하기 위해서는 인스턴스객체를 만들어야한다.
print(k.getname()) #자기가 가지는 인스턴스객체 값을 가지고 옴

print(Klass.getname(k)) # 클래스가 가지고 있는 getname에 우리가 쓰고자하는 데이터를 넣어주어도됨

instance attr

instance attr

import pprint

pprint.pprint(Klass.__dict__)

mappingproxy({'__dict__': <attribute '__dict__' of 'Klass' objects>,
              '__doc__': None,
              '__init__': <function Klass.__init__ at 0x79db0211f7f0>,
              '__module__': '__main__',
              '__weakref__': <attribute '__weakref__' of 'Klass' objects>,
              'getname': <function Klass.getname at 0x79db0211f910>,
              'name': 'Klass attr'})

print(k.getname) # 결과로는 메서드의 반환 값이 출력
print(Klass.getname) # 메서드 자체가 출력

<bound method Klass.getname of <__main__.Klass object at 0x79db0214ce50>>

<function Klass.getname at 0x79db0211f910>

 

2. 생성자(Constructor)와 소멸자(Destructor)

2.1 생성자 이해하기: __ new __

  • 클래스 생성자로 인스턴스 생성하기
  • 우리는 __init__을 많이 사용하는데 그것은 __init__이 생성자처럼 인스턴스 초기화 작업을 자동으로 처리해주기 때문 , 별도로 특정 기능을 지정해서 사용하려면 __new__ 생성자를 이용해서 사용한다.
  • __new__는 새로운 인스턴스를 생성한 후 그 인스턴스를 __init__에게 넘겨주는 역할을 한다.

 

 

  • __new__: 클래스로부터 새로운 인스턴스를 생성하는 메서드. 주로 상속받는 클래스를 만들 때나 불변 객체를 만들 때 오버라이드합니다.
  • __init__: 생성된 인스턴스를 초기화하는 메서드. 인스턴스 변수 설정 등의 초기화 작업을 처리합니다.

 

import pprint

class AAA :
    def __new__(cls) :
        return object.__new__(cls)

aaa = AAA()

print(aaa)

<__main__.AAA object at 0x79db0214fa00>

# __new__ 메서드는 기본 객체 생성 메서드를 호출하여 새로운 인스턴스를 생성하고, 이를 반환하여 aaa 변수에 할당

pprint.pprint(AAA.__dict__)

mappingproxy({'__dict__': <attribute '__dict__' of 'AAA' objects>,
              '__doc__': None,
              '__module__': '__main__',
              '__new__': <staticmethod(<function AAA.__new__ at 0x79db0211e290>)>,
              '__weakref__': <attribute '__weakref__' of 'AAA' objects>})

print(isinstance(aaa,AAA))

True

 

  • 자기 자신의 클래스만 처리
  • 생성할려고 할때 상위클래스 기능들이 동작하는 경우가 많은데, 그 틀을 막고 자기 자신만 처리하려고할때 이용
  • __new__ 메서드를 사용하여 인스턴스 대신 클래스 자체를 반환함으로써, 인스턴스 생성 과정을 막고, 클래스 자체를 처리하는 방법,  이를 통해 상위 클래스의 기능들을 사용하지 않고, 특정 상황에서 자신만의 클래스를 처리하는 방법을 구현할 수 있다.
class OnlyKlass :
    def __new__(cls) :
        return cls # 여기서 cls는 클래스를 의미하며, 인스턴스가 아닌 클래스 자체를 반환합니다.

ok = OnlyKlass()

print(ok)

<class '__main__.OnlyKlass'>

print(type(ok))
print(ok is OnlyKlass)

<class 'type'>

True

 

  • OnlyKlass 클래스의 __new__ 메서드를 사용하여 인스턴스 대신 클래스를 반환하고, 클래스 메서드를 통해 클래스 속성을 설정하고 가져오는 방법
  • OnlyKlass 클래스의 구조와 메서드가 클래스 딕셔너리 내에 어떻게 저장되는지 확인
  • getname과 setname 클래스 메서드가 정의되어 있는 것을 확인함
class OnlyKlass :
    def __new__(cls) :
        return cls

    @classmethod
    def getname(cls) :
        return cls.name

    @classmethod
    def setname(cls, name) :
        cls.name = name
import pprint

pprint.pprint(OnlyKlass.__dict__)

mappingproxy({'__dict__': <attribute '__dict__' of 'OnlyKlass' objects>,
              '__doc__': None,
              '__module__': '__main__',
              '__new__': <staticmethod object at 0x0000000004B50588>,
              '__weakref__': <attribute '__weakref__' of 'OnlyKlass' objects>,
              'getname': <classmethod object at 0x0000000004B509B0>,
              'setname': <classmethod object at 0x0000000004B509E8>})

ok = OnlyKlass()

ok.setname(" class method ")
print(ok.getname())

class method

 

2.2 초기화 메서드 사용하기: __ init __

  • 인스턴스 속성을 초기화
class Pass :
    pass

p = Pass()

print(p.__dict__) # 인스턴스 생성 직후에는 __dict__가 비어 있음, p생성시 어떠한 속성도 초기화되지 않았기 때문
p.name = "금곡초"
p.age = 10

print(p.__dict__# 속성을 추가한 후에는 __dict__에 추가된 속성들이 나타남 {}

{'name': '금곡초', 'age': 10}

class INIT :
    count = 0
    def __init__(self,name,age) : # 초기화시 두 값을 넣어달라고 지정했는데, 그냥 (self)로 하면 따로 받는게 없어서 가능
        self.name = name
        self.age = age
        self.count += 1

i = INIT() # 두 값을 지정 하지 않아서 TypeError 발생

TypeError: __init__() missing 2 required positional arguments: 'name' and 'age'

class INIT :
    count = 0
    def __init__(self,name,age) : 
        self.name = name
        self.age = age
        self.count += 1

i = INIT("금곡중",15)
print(i.__dict__)

{'name': '금곡중', 'age': 15, 'count': 1}

print(i.name)
print(i.age)
print(i.count)

금곡중

15

1

2.3 소멸자 메서드 사용하기: __ del __

  • 소멸자 정의된 클래스 생성하기

각 인스턴스는 초기화될 때마다 Counter.count가 증가하며, 삭제될 때마다 감소

class Counter :
    count = 0

    def __init__(self,name) :
        self.name = name
        Counter.count = Counter.count +1

    def __del__(self) :
        Counter.count = Counter.count -1
x = Counter(" First ")
print(x)
print(x.__dict__)
print(Counter.count)

<__main__.Counter object at 0x79db0214d480>
{'name': ' First '}
1

y = Counter(" Second ")
print(y)
print(y.__dict__)
print(Counter.count)

<__main__.Counter object at 0x79db0214c5e0>
{'name': ' Second '}
2

 

del가 한번 호출될때마다 -1 시켜줌으로 2-1 인 1이 나와야한다.

del y
print(Counter.count)

1

  • 약한 참조 이용하기
a = 1
print(id(a))
b = a
print(id(b))

del a
print(b)

1575533008

1575533008

1

 

약한 참조를 사용하여 객체를 참조하고, 객체가 존재할 경우 해당 객체의 메서드를 호출하는 방법

  • assert 문은 조건을 검사하고, 조건이 거짓일 경우 프로그램이 예외를 발생시키는 Python의 내장 명령문
import weakref
import gc

class MyObject(object):
    def my_method(self):
        print('my_method was called!')

obj = MyObject()
r = weakref.ref(obj) # obj를 약한 참조로 만듬
print(type(r), r) # r의 타입과 값을 출력
s = r() # 약한 참조를 통해 실제 객체에 접근
assert isinstance(obj, MyObject) # obj가 MyObject 클래스의 인스턴스인지 확인
assert s is obj #s와 obj가 같은 객체인지 확인 (같지 않으면 문제를 출력)

s.my_method() # s를 통해 객체의 메서드를 호출

<class 'weakref.ReferenceType'> <weakref at 0x79db1f4c4b80; to 'MyObject' at 0x79db0214dbd0> my_method was called!

 

obj = 1 # obj가 MyObject가 아니라 다른 정보를 덮어 씌운것을 의미
gc.collect()
print(r)
print(s) # obj가 기능이 없어졌으니 해당 내용 확인
 
assert r() is not  None  # 만약 사라지지 않았다면 not None인 것이다.

<weakref at 0x79db1f4c4b80; to 'MyObject' at 0x79db0214dbd0>

<__main__.MyObject object at 0x79db0214dbd0>

2.4 생성자 작동 원리 이해하기

  • 생성자, 초기화, 호출연산자를 정의한 클래스
class MDPerson(object) :
    def __new__(cls,name,major) : 
        return object.__new__(cls)

    def __init__(self,name, major) : # 인스턴스 초기화 메서드
        self.name = name
        self.major = major

    def __call__(cls, name, major) : # 클래스를 호출할 때 실행
        print(" __new__ ")
        self = cls.__new__(cls,name,major) # __new__ 메서드 호출하여 객체 생성
        print(" __init__ ")
        self.__init__(name,major) # __init__ 메서드 호출하여 객체 초기화
        return self
# MDPerson 클래스를 __call__ 메서드를 통해 호출하여 객체를 생성
mdp = MDPerson.__call__(MDPerson,"춘식이","quant")
print(mdp)
print(mdp.name)
print(mdp.major)

 __new__ 
 __init__ 
<__main__.MDPerson object at 0x79db0214f4f0>
춘식이
quant

 

type 이용

mdp2 = type.__call__(MDPerson,"춘장이","quant")

print(mdp2)
print(mdp2.name)
print(mdp2.major)

<__main__.MDPerson object at 0x79db0214de40>

춘장이

quant

mdp3 = MDPerson("춘장삼","quant")

print(mdp3)
print(mdp3.name)
print(mdp3.major)

<__main__.MDPerson object at 0x79db0214d5d0>
춘장삼
quant

2.5 함수를 이용한 생성자 패턴

  • 함수의 결과로 인스턴스 생성하기
class Person :
    def __init__(self,name,age) :
        self.name = name
        self.age = age
class Employee(Person):
    def __init__(self, name, age, depart,salary) :
        super().__init__(name,age)
        self.depart = depart
        self.salary = salary
class Employer(Person) :
    def __init__(self, name, age, salary) :
        super().__init__(name,age)
        self.salary = salary
def employ(name,age, *,depart=None,salary=None) :
    if depart is None :
        return Employer(name,age,salary=salary)
    else :

        if salary == None :
            salary = 0

        return Employee(name,age,depart=depart,salary=salary)
# employ 함수를 사용하여 Employee 인스턴스 생성
e = employ("춘식이",31,depart="빅데이터부",salary=30000)

print(e)
print(type(e))

<__main__.Employee object at 0x79db0214d090>

<class '__main__.Employee'>

 

depart가 None이 들어간 경우

e = employ("홑씨",52,salary=300000)

print(e)
print(type(e))

<__main__.Employer object at 0x79db0214fd60>

<class '__main__.Employer'>

 

2.6 인스턴스 네임스페이스 변경하기: __ slots __

  • __ slots __ 사용하기
  • __ slots __  : 파이썬 클래스에서 인스턴스 속성을 제한하는 메커니즘
  • 일반적으로 파이썬 클래스는 인스턴스의 속성을 동적으로 추가할 수 있지만, __slots__를 사용하면 특정 클래스의 인스턴스가 가질 수 있는 속성을 제한할 수 있다. (네임 스페이스 내용 관리에 용이)
class Klass :
    __slots__ = ("name",)
    def __init__(self, name,age) :
        self.name = name
        self.age  = age

k = Klass("name","age")

AttributeError: 'Klass' object has no attribute 'age' # 슬롯에 지정이 않되어 있어서 age에 의해 에러남

이처럼 슬롯을 이용해서 네임스페이스 안에 내용을 제한할 수 있음

class Klass :
    __slots__ = ("name",)
    def __init__(self, name) :
        self.name = name

k = Klass("name")
print(k)

<__main__.Klass object at 0x79db0214d210>

class Klass :
    __slots__ = ("name",)
    def __init__(self, name) :
        self.name = name

k = Klass("name")
print(k)

<__main__.Klass object at 0x79db0214c670>

 

print(k.name)
print(k.__dict__)

name

AttributeError: 'Klass' object has no attribute '__dict__'

만들어진것 보기
import pprint

pprint.pprint(dir(Klass))

['__class__',

 '__delattr__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__module__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__slots__',
 '__str__',
 '__subclasshook__',
 'name']

 

여기있는 클래스에서 반듯이 필요한 것들은 다 들어있음, __dict__는 빠져있는 것을 볼 수 있다. (dict는 클래스에서 반듯이 필요한 부분은 아니다.) - 슬롯이 딕트를 대체했다(네임스페이스를 관리해주던것이 딕트였으니까)

 

  • __slots__를 사용하면 메모리 사용량이 줄어들고 객체 접근 속도가 약간 향상될 수 있습니다. 그 이유는 객체의 속성을 딕셔너리 대신 튜플 형태로 관리하기 때문입니다.
  • 하지만 __slots__를 사용하면 동적으로 속성을 추가할 수 없으며, 인스턴스에 정의된 속성만을 사용할 수 있습니다.
  • 따라서 __dict__가 없는 경우에는 해당 클래스의 인스턴스는 __slots__에 명시된 속성만을 가지고 있음을 의미합니다.
  • __slots__를 사용하면 객체의 메모리 효율성을 높이고 속성 접근 속도를 개선할 수 있지만, 동적 속성 추가 기능이 필요한 경우에는 __dict__를 사용하는 일반적인 클래스 정의 방식을 사용해야 합니다.

 

print(Klass.__slots__)
print(Klass.__dict__[Klass.__slots__[0]])

('name',)

<member 'name' of 'Klass' objects>

print(type(Klass.name))

<class 'member_descriptor'>

 

  • 관행적으로 __ dict __ 속성 조회 로직이 있을 경우
class MyClass :
    __slots__ = ['x','y','__dict__']

    def __init__(self,x,y) :
        self.x = x
        self.y = y
        self.__dict__ = {}
m = MyClass(5,5)
print(m.x, m.y)
print(m.__dict__)

5 5

{}

 

3. 객체 접근 연산(.)

3.1 점(dot) 연산

 
  • 초기화 처리할 때 속성을 접근
class Person :

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

    def __setattr__(self, name, value) :
        print(" __setattr__ ", name)
        self.__dict__[name] = value
        print(value)
p = Person("사람", 50)

__setattr__ name

사람

__setattr__ age

50

3.2 점 연산자 스페셜 메서드(Special Method) 기본 이해하기

  • __ getattribute __를 이용해서 클래스 내부 검색
    job = "즐기기"

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

    def __getattribute__(self, name) :
        print(" attribute name ", name)
        return super().__getattribute__(name)

    def __getattr__(self, name) :
        print(" attr name ", name)
        return Person.__dict__[name]
p = Person("긍정",55
print(p.name) # 프린트에서 위 내용이 순서대로 출력되고 있는 것이다.

attribute name name

긍정

p.job
 attribute name  job
즐기기
 
p.__getattr__('job')

 attribute name  __getattr__
 attr name  job
즐기기

  • attrgetter를 이용해서 속성 접근
  • operator.attrgetter 함수는 주어진 객체에서 속성을 가져오는 함수를 생성
class Person :

    job = "즐기기"

    def __init__(self, name, age) :
        self.name = name
        self.age  = age
import operator

getname = operator.attrgetter("name","age","job") # attrgetter를 사용하여 name, age, job 속성을 가져오는 함수를 생성

p = Person("가을이",10)

print(getname)
# attrgetter를 사용하여 p 인스턴스의 속성들을 가져옴
print(getname(p))

operator.attrgetter('name', 'age', 'job')

('가을이', 10, '즐기기')

728x90

'PYTHON-BACK' 카테고리의 다른 글

#파이썬 기초 9일차_1  (0) 2024.07.09
#파이썬 기초 8일차_2  (0) 2024.07.08
#파이썬 기초 7일차_2  (1) 2024.07.05
#파이썬 기초 7일차_1  (1) 2024.07.05
#파이썬 기초 6일차_1  (0) 2024.07.04