파이썬 문법 overview

28444 단어 파이썬파이썬

인덴트, 네이밍 컨벤션, 타입힌트, 리스트 컴프리핸션, 제너레이터, range, enumerate, // 나눗셈 연산자, print, pass, locals 에 대하여 전반적으로 살펴보겠습니다

인덱스

공백 4칸을 원칙으로 한다.

네이밍 컨벤션

파이썬의 변수명 네이밍컨벤션은 자바와 달리 각 단어를 밑줄(_)로 구분하여 표기하는 스테이크 케이스를 따른다.
ex. my_class

↔ 카멜 케이스 : 대소문자를 구별하여 표기하는 방식
ex.MyClass

타입힌트

파이썬은 대표적인 동적 타이핑 언어이지만, 타입을 지정할 수 있는 타입힌트가 PEP 484에서 추가되었다. 파이썬 버전 3.5부터 사용할 수 있다.

기존에는 타입 힌트를 사용하지 않았지만(이는 빠르게 정의하여 사용할 수 있다는 장점이 있다) 함수의 파라미터 a에는 숫자를 넘겨야 하는지, 문자를 넘겨야 하는지 전혀 알 수 없으며, 이 함수의 리턴값(타입)이 무엇인지도 알 수 없다.

이는 나중에 프로젝트의 규모가 커지게 되면 가독성을 떨어뜨리게 되며 무엇보다 버그유발의 주범이 된다.

a: str ="1"
b: int = 1

def fn(a: int) -> bool:
  return True

이처럼 타입 힌트를 사용하게 되면 이제 fn()함수의 파라미터가 a가 정수형임을 분명하게 알 수 있으며 리턴 값으로 True 또는 False를 리턴할 것이라는 점도 확실히 알 수 있다.

이점🎈

  1. 명시적 선언으로 인해 가독성이 좋아짐
  2. 버그 발생 확률 줄일 수 있다.
    (주의) 강제 규약이 아니다 보니, 여전히 동적으로 할당될 수 있음.

리스트 컴프리헨션

1. map함수

map은 리스트의 요소를 지정된 함수로 처리해주는 함수이다(map은 원본 리스트를 변경하지 않고 새 리스트를 생성한다).

  • list(map(함수,리스트))
  • tuple(map(함수,투플))

함수는 단순히 형변환도 가능하다 ( int,str 등 )

# map 적용 안했을때
a=[1.2,3.3,4.5]

for i in range(len(a)) :
  a[i]=int(a[i])
print(a)
# map 적용 했을때 
b= list(map(int,a))
print(b)

map()의 출력결과는 iterator이기 때문에 아래와 같이 맞는 결과인지 확인 할 수 없다. next를 이용하여 하나씩 확인해보아도 되지만 대부분 형변환(list나 tuple등)을 하여 결과를 확인한다.

target=[2.2,3.3,4.4]
hello=map(lambda x: x+1, target)
print(hello)

print(list(hello))

2. filter 함수

map함수와 사용방법은 동일하나, 함수의 값이 참이냐 거짓이냐에 따라 해당요소를 포함할 지 결정한다

# filter 적용 안했을 때
target = [ 1,2,3,4,5,6,7 ]
new=[]

def is_even(num: int) -> bool:
  if num%2 ==0 :
    return True
  else :
    return False

for i in target :
  if is_even(i):
    new.append(i)
  
print(new)

# filter 적용 했을 때
target=[1,2,3,4,5,6]
a= filter(lambda x: x%2==0, target)
print(a)
print(list(a))
#print(list(a[2])) 이건 안됨..

b=list(filter(lambda x: x%2==0, target))
print(b)
print(b[2])

3. 리스트 컴프리헨션

기존 리스트를 기반으로 새로운 리스트를 만들어내는 구문
(딕셔너리도 가능)

# 리스트 컴프리헨션을 사용한 경우
[n*2 for n in range(1, 10+1) if n%2 ==1 ]

# 리스트 컴프리헨션을 사용하지 않은 경우
a = []
for n in range(1,10+1):
  if n%2 == 1 :
    a.append(n*2)

제너레이터

루프의 iteration 동작을 제어할 수 있는 루틴 형태
ex. 임의의 조건으로 숫자 1억개를 만들어내는 프로그램 작성

  • 제너레이터 x : 메모리 어딘가에 숫자 1억 개를 보관해야 함
  • 제너레이터 o : 단순히 제너레이터만 생성해두고 필요할 때 언제든 생성 가능

yield 구문을 사용하면 제너레이터를 리턴할 수 있음.
↔ 기존의 함수는 return 구분을 맞닥뜨리면 값을 리턴하고 모든 함수의 동작 종료

def get_natural_number() :
  n=0
  while True :
    n +=1
    yield n
    
get_natural_number()
#output : <generator object get_natural_number at 0x7fb01d6ffb50>

만약 다음 값을 생성하려면 next()로 추출하면 된다. 예를 들어 100개의 값을 생성하고 싶다면 다음과 같이 100번동안 next()를 수행하면 된다.

g=get_natural_number()
for _ in range(0,100):
  print(next(g))

아울러 제너레이터는 다음과 같이 여러 타입의 값을 하나의 함수에서 생성하는 것도 가능하다.

def generator() :
  yield 1
  yield 'string'
  yield True
g= generator()
g
# 출력 : <generator object generator at 0x7fb01d5d2c50>

print(next(g))
print(next(g))
print(next(g))
# 출력 : (차례대로) 1, 'string', True

range

제너레이터 방식을 활용하는 대표적인 함수로 range()가 있다.

print(list(range(5)))
print(range(5))
print(type(range(5)))
for i in range(5):
  print(i,end=' ')
a=[n for n in range(10000)]
# 출력 : 0부터 10000까지의 값이 모두 저장되어 출력
b= range(10000)
# 출력 : range(0,10000)

a

b

a는 이미 생성된 값이 담겨있고, b는 생성해야 한다는 조건만 존재한다.
이제 둘 사이의 메모리 점유율을 비교해보면 range 클래스를 리턴하는 방식의 장점이 쉽게 와닿을 것이다.

import sys
sys.getsizeof(a)
# 출력 : 87632
sys.getsizeof(b)
# 출력 : 48

enumerate

순서가 있는 자료형(list,tupel,set등)을 인덱스를 포함한 emurate객체로 리턴한다.

a=[1,2,3,2,45,2,5]
a

b=list(enumerate(a))
# list()로 결과를 추출할 수 있는데, 인덱스를 자동으로 부여해주기 때문에 매우 편리하게 사용할 수 있다.
print(b)
# 출력 : [(0,1),(1,2),(2,3),(3,2),(4,45),(5,2),(6,5) ]
print(b[4])
# 출력 : (4,45)
# enumerate를 사용하지 않는다면?
for i in range(len(a)):
  print(i,a[i])
# 불필요한 a[i] 조희작업과 전체길이를 조회해야한다는 점이 있다.

// 나눗셈 연산자

  • //연산자는 몫을 구하는 연산자이다
5//3 # int(5//3)과 동일한 표현식이다.
# 출력 : 1

5/3
# 출력 : 1.66666...

5%3
# 출력 : 2

divmod(5,3) #몫과 나머지를 한 번에 구할 때
# 출력 : (1,2)

print

print('A1','A2') #자동 띄어쓰기로 값을 구분해줌
# 출력 : A1 A2
print('A1','A2', sep=',') # 구분자를 콤마(,)로 지정해줄 수도 있다.
# 출력 : A1,A2

# print()는 항상 줄바꿈을 해준다.
print('aa', end=' ')
print('bb')
# 출력 : aa bb

# 리스트를 출력할 때는 join()으로 묶어서 처리한다.
a = ['A', 'B']
print(' '.join(a))

# format과 f-string 방법
print('{0}:{1}'.format(2,"str"))
print(f'{2}:{"str"}')
# 출력 : 2:'str'

pass

코딩을 하다보면 일단 코드의 전체 골격을 잡아놓고 내부에서 처리할 내용은 차근차근 생각하며 만들겠다는 의도로 다음과 같이 코딩하는 경우가 있다.

class MyClass(object):
  def method_a(self):
    pass
  def method_b(self):
    pass

구글 파이썬 스타일 가이드

함수의 기본값으로 가변객체(mutable object)를 사용하지 않아야 한다. 함수가 객체를 수정하면 기본값이 변경되기 때문이다. 따라서 다름과 같이 기본값으로 []나 {}를 사용하는 것은 지양해야 한다.

# 지양
def foo(a,b=[]):
  pass

def foo(a,b ={}):
  pass

대신 다음과 같이 불변 객체(immutable object)를 사용한다. None을 명시적으로 할당하는 것도 좋은 방법이다.

#지향
def foo(a,b=None):
  pass

def foo(a,b=None):
  if b==None:
    b=[]
    pass

Ture, False를 판별할 때는 암시적(implicit)인 방법을 사용하는 편이 간결하고 가독성이 좋다.
ex. len(users) ==0 은 not users 로 충분하다

그러나 정수를 처리할 때는 암시적이기 보다는 직접 비교하는 편(명시적 작성)이 덜 위험하다. 모듈로 연산 결과가 0인 것을 정수로 처리하지 않고 암시적 거짓 여부로 판별하는 위험하기 때문이다.

좋은 웹페이지 즐겨찾기