클래스는 사용자 정의 자료형이다.
인스턴스는 클래스 기반으로 생성된 객체이다.
- method : 클래스 내부에 선언된 함수로 class나 instance를 가지고 호출
- attribute : 클래스나 인스턴스에서 사용하는 데이터
- Accessor : 속성의 값을 사용하기 위해 리턴하거나 설정하는 메서드(권장)
#클래스 생성 class Student: def disp(self): print("인스턴스 메서드") #만들어지기는 클래스에 만들어졌지만 #인스턴스 없이는 호출할 수 없는 메서드 def setName(self, name): self.name=name #name이란 속성이 없으면 만들어서 대입, 존재하면 수정 def getName(self): return self.name stu1=Student() #인스턴스 생성 #메서드 호출 Student.disp(stu1)#첫 자리에 인스턴스를 넣어야함 #클래스가 인스턴스의 메서드 호출 : unbound 호출 stu1.disp()#self에 인스턴스가 대입됨 - bound 호출 Student.setName(stu1,"mino") print(Student.getName(stu1)) print(stu1.getName())
대표적인 속성
__doc__
: 함수를 설명하기 위한 속성
__name__
: 함수의 이름
생성자를 만드는 이유
- 처음부터 속성을 만들거나 속성을 원하는 값으로 초기화하기 위해
- python에서는 생성자를 만들 때, __init__ 이라는 이름을 사용
#클래스 생성 class Student: #생성자 - 인스턴스를 생성할 떄 호출하는 메서드 #이 메서드에서 속성을 생성해서 속성을 처음부터 #소유하도록 만들어주는 것이 좋습니다. #매개변수를 이용해서 초기화하면, 만들어질 때 #다양한 값으로 초기화가 가능합니다. def __init__(self,name="noname"): #필수 요소는 아니면 기본값을 주고, #필수 요소이면 기본값을 안주고 달라고 한다. #name값을 안주면 noname이 들어감 print("인스턴스 생성") #self.name="기본값" 특정 상수로 생성하는 경우에는 #생성자 내부에서 직접 설정, 권장 x self.name=name#이제 name을 받아서 만듦 #매개변수를 이용해서 초기화 할 때, #매개변수의 기본값을 설정하지 않으면 #인스턴스를 생성할 때, 반드시 매개변수에 값을 대입해야 한다. #소멸자 - 인스턴스가 소멸될 때 호출되는 메서드 def __del__(self): print("인스턴스 소멸") def disp(self): print("인스턴스 메서드") #만들어지기는 클래스에 만들어졌지만 #인스턴스 없이는 호출할 수 없는 메서드 def setName(self, name): self.name=name #name이란 속성이 없으면 만들어서 대입, 존재하면 수정 def getName(self): return self.name stu1=Student("mino2") #인스턴스 생성 #메서드 호출 #Student.disp(stu1)#첫 자리에 인스턴스를 넣어야함 #클래스가 인스턴스의 메서드 호출 : unbound 호출 stu1.disp()#self에 인스턴스가 대입됨 - bound 호출 print(Student.getName(stu1)) print(stu1.getName())
class Student(): #클래스 속성 - 클래스에서 1개만 생성 auto_increment=0 def __init__(self): #메서드 안에서 클래스 속성은 아래와 같이 부른다. Student.auto_increment+=1 self.no=Student.auto_increment stu1=Student() print(stu1.no) stu2=Student() print(stu2.no)
Reference Count 확인하는 방법
- sys 모듈의 getrefcount라는 함수에 인스턴스를 대입하면 됩니다.
- 이때, 1이 증가해서 나오게 되는데, 이는 getrefcount가 한 번 참조해서 그런 것입니다.
class Student(): #클래스 속성 - 클래스에서 1개만 생성 auto_increment=0 def __init__(self): #메서드 안에서 클래스 속성은 아래와 같이 부른다. self.no=Student.auto_increment Student.auto_increment+=1 def __del__(self): print("인스턴스 소멸") stu1=Student() #인스턴스가 생성되고 참조 카운트가 1이 됩니다. stu1=None#참조를 가리키는 변수에 None을 대입하면 #참조 카운트가 1감소하며, 값이 0이 된다면 인스턴스가 소멸됩니다 stu2=Student() #참조 카운트 1 stu3=stu2 # 참조 카운트 2 ( 다른 변수에 참조를 대입) stu2=None # 참조 카운트 1감소 ( 1이라서 소멸은 안됨 ) print("프로그램 종료")
@staticmethod
라는 decorator를 이용해서 생성@classmethod
라는 decorator를 이용해서 생성class Student(): #클래스 속성 - 클래스에서 1개만 생성 auto_increment=0 def __init__(self): #메서드 안에서 클래스 속성은 아래와 같이 부른다. self.no=Student.auto_increment Student.auto_increment+=1 print(self.no) def __del__(self): print("인스턴스 소멸") @staticmethod def method():#static method는 self가 없다. print("매개변수가 없는 static method") #Student.method() #인스턴스를 만들 필요가 없는 static method #stu1=Student() #인스턴스가 생성되고 참조 카운트가 1이 됩니다. #stu1=None#참조를 가리키는 변수에 None을 대입하면 #참조 카운트가 1감소하며, 값이 0이 된다면 인스턴스가 소멸됩니다 stu2=Student() #참조 카운트 1 stu3=stu2 # 참조 카운트 2 ( 다른 변수에 참조를 대입) stu2=None # 참조 카운트 1감소 ( 1이라서 소멸은 안됨 ) print("프로그램 종료")
class Student(): auto_increment=0 @classmethod def method(self): Student.auto_increment=100 Student.method()
class Student(): #name과 age 속성만 사용하도록 제한하고 싶다. __slots__=["name","age"] stu1=Student() stu1.name="mino" stu1.age=26 stu1.job="student" #이래도 파이썬은 기본적으로 에러가 안남 #하지만 slots로 제한하면 job은 만들 수 없음
class Student(): def __init__(self): self.name="mino" self.__no=1 #이렇게 만들면 인스턴스는 속성에 직접 접근 불가 #메서드를 통해서만 접근 가능 stu1=Student() print(stu1.name) print(stu1.__no)
프로퍼티이름 = property(fget=None, fset=None, fdel=None, doc=None)
getter, setter, del 등을 매개변수에 연결하자.
class Student(): def __init__(self, name="noname"): self.__name=name #속성 이름이 __로 시작하기에 인스턴스 접근물가 #접근자 메서드 def getName(self): print("name의 getter 호출") return self.__name def setName(self,name): print("name의 setter 호출") self.__name=name #property #name을 호출하면 getName 메서드가 호출되고, #name에 값을 대입하면 setName 메서드가 호출된다. name=property(fget=getName, fset=setName) stu=Student() stu.setName("mino") print(stu.getName()) stu.name="mino2" print(stu.name)
name에 값을 직접 넣는 것(속성을 바로 이용하는 것) 처럼 보이지만,
메서드를 호출하는 것이다.
class Student(): def __init__(self, name="noname"): self.__name=name #속성 이름이 __로 시작하기에 인스턴스 접근물가 #접근자 메서드 @property #getter설정 def name(self): print("name의 getter 호출") return self.__name @name.setter def name(self,name): print("name의 setter 호출") self.__name=name stu=Student() stu.name="mino2" print(stu.name)
getter는 property, setter는 이름.setter라고만 하면 된다.
하고 getName, setName이 아닌 property 이름으로 def해주자.
method의 이름은
__이름__
입니다.
+
는__add__
==
는__eq__
하지만 연산자 오버로딩을 할 때, 매개변수의 개수는 변경이 불가능합니다.
class Student: def __init__(self,name="noname"): self.name=name #+ 연산자 오버로딩 def __add__(self,other): return self.name+other.name #name을 결합하겠다. #==연산자 오버로딩 def __eq__(self, other): return self.name==other.name #name만 같으면 같다.(id 확인x) stu1=Student("mino") stu2=Student("mino") #stu1, stu2 는 공간할당을 받고 id가 있는데 # 두 id는 다르다. print(stu1+stu2) # 당연히 안된다. stu3=stu1 print(stu1==stu2) #오버로딩을 하니까 얘는 #True(왜냐면 단순 요소비교) print(stu1 is stu2)
python은 연산자의 기능을 바꿀 수 있다.
class Student: def __init__(self, name="noname", count=8): self.name=name self.count=count def __add__(self, other): return self.count+other.count stu1=Student("사과",3) stu2=Student("포도",2) print(stu1.count+stu2.count)#얘도 나오긴해 print(stu1+stu2) #이러면 끝이긴 해
단일 상속 : 하나의 클래스로부터 물려받는 것
다중 상속 : 두 개 이상의 클래스로부터 물려받는 것
class 클래스이름(상위클래스 이름나열):
class Sup: def superMethod(self): print("상위 클래스의 메서드입니다.") #Sup클래스를 상속받는 Sub 클래스 class Sub(Sup): def subMethod(self): print("하위 클래스의 메서드입니다.") #Sub의 인스턴스를 생성해서 필요한 메서드 호출 my=Sub() my.superMethod() #Sub클래스에는 없지만, Sup를 상속받았기 때문에 호출이 가능하다. my.subMethod()
__init__
함수는 super().__init()__
으로 호출__init__
이외의 함수는 super().메서드이름
으로 호출합니다.class Sup: def __init__(self): self.name="noname" def superMethod(self): print("상위 클래스의 메서드입니다.") #Sup클래스를 상속받는 Sub 클래스 class Sub(Sup): #def __init__(self): # self.score=80 def __init__(self): super().__init__()#이 작업이 무조건 필요함 self.score=80 def subMethod(self): print("하위 클래스의 메서드입니다.") #Sub의 인스턴스를 생성해서 필요한 메서드 호출 my=Sub() my.superMethod() #Sub클래스에는 없지만, Sup를 상속받았기 때문에 호출이 가능하다. my.subMethod() print(my.name) #하위 클래스에서 __init__를 생성하면 #상위 클래스의 __init__을 호출하지 않는다. #하위 클래스의 __init__을 만들 때에는 #상위 클래스의 __init__을 호출해주어야 합니다.
오버라이딩을 할 거면 호출해라.
class Sup: def method(self): print("상위 클래스의 메서드") #Sup클래스를 상속받는 Sub 클래스 class Sub(Sup): #상위 클래스에 존재하는 메서드를 하위 클래스에서 #다시 정의 - Overrriding #상위 클래스 안써도 에러는 안나네? def method(self): super().method() #상위 클래스의 메서드 호출은 잊지말자 print("하위 클래스의 메서드") s=Sub() s.method()
Q: 하위 클래스에서 상위 클래스 print 빼고 하위 print만 남긴다면 그건 overriding인가?
기능의 확장이어야 하기 때문에 아닐 것 같다.
다중 상속을 했을 때, 메서드를 찾는 순서
클래스이름.mro()
super(클래스이름,self)
를 대입해야 합니다.
- 추상 메서드
- 메서드의 내용이 없이 이름만 존재하는 경우
- 메서드 상단에@abstractmethod
를 추가하면 추상메소드 생성- 추상 클래스
- 추상 메서드를 소유한 클래스로, 자신의 인스턴스는 생성할 수 없고, 상속을 통해서만 사용하는 클래스
-abc Module import
한 뒤, class의 ()안에metaclass=ABCMeta
를 추가
import abc #추상 클래스 - 자신의 인스턴스를 생성할 수 없음 class AbstractClass(metaclass=abc.ABCMeta): #추상 메서드 - 내용이 없는 메서드로 #하위 클래스에서 구현해서 사용해야 합니다. @abc.abstractmethod def method(self): pass class Sub(AbstractClass): def __init__(self): print("인스턴스 생성") #추상 클래스를 상속받는 경우, 추상 메서드를 #반드시 implementation해주어야 합니다. #안해주면 에러가 납니다. def method(self): print("추상 메서드 구현") #inst=AbstractClass()#인스턴스 만들 수 없어서 에러! #상속을 통해서만 사용해야 한다. instance=Sub() instance.method()
__getattr__(self,name)
메서드를 재정의하면 됩니다.
name에는 호출하는 함수가 전달됩니다.
__iter__
메서드와___next___
메서드를 재정의해서 구현
원래 Module이라는 단어는 독립적으로 수행되는 코드 블럭이며,
Python에서는 하나의 파일을 Module이라고 합니다.
import 모듈이나 패키지 이름
모듈이름 또는 패키지 이름.구성요소이름
을 쓰면 됩니다.math 모듈에 있는 abs라는 함수를 사용하고자 한다.
import math
math.abs()
하면 됩니다.
python 은 내가 사용하지 않는 모듈을 import해도 오류는 아니지만, 메모리 낭비이다.
from module이나 package이름 import 구성요소
from math import abs
abs()
이렇게 사용 가능합니다.
단, math에서 abs만 가져오는 것입니다.
이것을 더 권장합니다.
from 모듈이나 패키지이름 import *
from math import *
abs()
cos()
모든 구성요소를 추가하는 것입니다.
import 모듈이나 패키지 이름 as 다른 이름
import pandas as pd
pandas 패키지를 이름을 pd로 바꿔서 패키지 추가하는 것.
pandas가 아닌 pd로 불려야 합니다.
__import __(모듈이나 패키지이름)
으로 사용가능이렇게 하는 것은 지양합시다.
import pandas
import pandas as pd
똑같은 것을 두 번 가지고 오게 됩니다.
import sys
print(sys.modules)
하면 잘 보입니다.
import sys
sys.path
sys.path.append("찾는위치")
를 해주면 됩니다.myPI=3.14 def func(message): print(message)
import myMath #myMath 모듈이나 패키지를 import import sys sys.path.append("./") #현재 디렉토리에서 모듈이나 패키지를 검색하도록 설정 myMath.func("모듈을 가져와서 프린트해봤어요") print(myMath.myPI)
파일을 나누어 작업을 할 수 있는 것을 확인했습니다.
__init__.py
파일이 존재하면 패키지로 간주합니다.pip install 패키지이름
pip install 패키지이름 -upgrade
pip install 패키지이름=버전
pip install 패키지이름>=버전
pip install 패키지이름 --user
!pip install 패키지이름
으로 패키지 설치를 합니다.pip list --format=columns
입력해보면 됩니다.#pip list --format=columns #matplotlib 이 있네요 #없다면? #pip install matplotlib import matplotlib.pyplot as plt fig=plt.figure(figsize=(10,7)) plt.boxplot(([10,87,29,76,88],[20,40,68,1000,87])) plt.grid() plt.show() fig.savefig("graph.png")
#실수 표현의 한계 때문에 2개 연산의 결과가 다르게 나옵니다. print((1234.567+45.67844)+0.0004) print(1234.567+(45.67844+0.0004))
이 둘의 결과가 다르다. 결국 실수로 연산하는 것은 매우 위험하다는 것을 알 수 있다.
from decimal import Decimal print((Decimal(1234.567)+Decimal(45.67844))+Decimal(0.0004)) print(Decimal(1234.567)+(Decimal(45.67844)+Decimal(0.0004))) print((Decimal('1234.567')+Decimal('45.67844'))+Decimal('0.0004')) print(Decimal('1234.567')+(Decimal('45.67844')+Decimal('0.0004')))
실수를 문자열로 만들어 Decimal 모듈을 사용하면 정확한 결과가 나옴을 확인할 수 있다.
함수
seed(x)
: 시드 설정random()
: 0부터 1사이의 소수가 리턴randint(min,max)
: 숫자 사이의 정수 리턴uniform(min, max)
: 숫자 사이의 실수 리턴randrange(시작,끝[, 간격])
gauss(m,sb)
: 가우스 분포의 난수shuffle(시퀀스)
: 시퀀스를 섞음choice(시퀀스)
: 랜덤한 데이터를 리턴합니다.(복원추출)sample(시퀀스,개수)
: 랜덤 데이터를 개수만큼 리턴(비복원 추출)이런 것이 나오면 help를 쳐보자. 뭐가 필요한지 직접 확인하기
import random random.seed(42)#seed를 고정해봤어. #help(random.randint) #randint(a, b) method of random.Random instance #Return random integer in range [a, b], #including both end points print(random.randint(1,45)) #seed를 고정하면 고정된 값이 나와 print(random.randint(1,45))
i= input("1부터 45까지의 정수를 공백으로 구분해서 6개 입력하세요") x=i.split(" ") lotto=list(map(lambda e: int(e),x)) lotto.sort() print(lotto) cnt=0 ar=range(1,46) #실행하지마 메모리터진다 while True: cnt+=1 gotcha=random.sample(ar,6) gotcha.sort() print(gotcha) if (lotto==gotcha): break else : print("실패!") print(cnt,"번 만에 로또에 당첨되었어요!")