python decoreator를 이해하기 위해서는 퍼스트 클래스 함수와 클로저를 이해하고 있어야 합니다. 만약 이해하고 계시지 않으시다면 밑의 강좌를 참고하여 주십시오.

파이썬 – 퍼스트클래스 함수 (First Class Function)
파이썬 – 클로저 (Closure)

 

데코레이터란 무엇일까요? 사전적 의미로는 “장식가” 또는 “인테리어 디자이너” 등의 의미를 가지고 있습니다. 이름 그대로, 자신의 방을 예쁜 벽지나 커튼으로 장식을 하듯이, 기존의 코드에 여러가지 기능을 추가하는 파이썬 구문이라고 생각하시면 됩니다.

 

Step1. 함수


>>> def foo():
...     return 1
...
>>> foo()
1

제일 기본이다. Python에 있어서 함수는 def 키워드로 함수명과 파라미터의 리스트(임의)를 이용해 정의한다. 또한 괄호를 붙인 이름을 지정하여 함수를 지정할 수 있다



Step2. 스코프


Python에서는 함수를 만들면 새로은 스코프가 만들어진다.  다시 말하자면 각각의 함수는 각각의 이름 공간을 가지고 있다는 의미이다.

Python에서 이것을 확인하기 위한 함수도 준비되어 있다. locals()라는 함수로 자신이 가진 로컬 이름 공간의 값을 dictionary 타입으로 반환한다.

locals() 함수는 현재의 local 변수를 dict type으로 던져주는 내장 함수입니다.

def foo(arg):
    x = 10
    print(locals())

foo(20)

## 실행결과
{'x': 10, 'arg': 20}

 또한 글로벌 이름 공간에 대해서는 globals() 함수로 확인가능하다.

y = 30
print(globals())

## 실행결과
{..., 'y': 30}

 

 

함수는 Python에 있어서 퍼스트 클래스 오브젝트이다.

Python에서는 함수는 객체입니다.

 

print(issubclass(int, object)) # Python내의 모든 객체는 같은 공통 클래스를 상속하여 만들어진다.
def foo():
    pass 

print(issubclass(foo.__class__, object))

## 실행결과
True
True

위 코드의 의미는, 함수를 일반적으로 다른 변수와 동일하게 취급한다는 것입니다. 즉, 다른 함수의 인수로 전달하거나 함수의 리턴값으로써 사용할 수 있다는 것입니다. 

def add(x, y):
    return x + y

def sub(x, y):
    return x - y
def apply(func, x, y):
    return func(x, y) 
    
print(apply(add, 2, 1))
print(apply(sub, 2, 1))

위의 예에서는 함수 apply는 파라미터로써 함수 add, sub를 인수로써 ( 다른 변수와 다르지 않게 ) 전달하고 있음을 알 수 있습니다. 

 

def outer():
    def inner():
        print("Inside inner")
    return inner # 1

foo = outer() #2
print(foo)

foo()

## 실행결과
<function outer.<locals>.inner at 0x000002BAEC7DEB00>
Inside inner

 위에서 살펴 봤던 코드와 다른 점은 #1에서 리턴값으로써 실행 결과를 보여주는 것이 아닌 함수 그 자체를 지정하고 있다는 것이다(inner() 가 아닌 inner 를 전달하고 있다).

 이것은 #2 와 같이 보통 대입가능하므로 foo에 함수를 넣어 실행하는 것이 가능하다는 것을 알 수 있다.

 

Step8. 클로저


 위에서 봤던 예를 살짝 변경해서 살펴보자.

>>> def outer():
...     x = 1
...     def inner():
...         print x
...     return inner
>>> foo = outer()
>>> foo.func_closure
(<cell at 0x...: int object at 0x...>,)

 inner는 outer에 의해 반환되는 함수로, foo에 저장되어 foo()에 의해 실행된다....는 말이 사실일까? Python의 변수 해결 규칙을 완전히 따르고 있는 말이지만, 라이프 사이클은 어떻게 되어 있을까? 변수 x는 outer함수가 실행되는 동안에만 존재한다. 여기서 outer함수는의 처리가 종료된 후에 inner함수가 foo에 대입하고 있으므로 foo()는 실행할 수 없는 것이 아닌가?

 그 예상과는 반대로 foo()는 실행가능하다. Python이 Function closure(클로저)의 기능을 가지고 있기 때문이다. 클로저는 글로벌 스코프 이외에 정의된 함수(예시의 경우에는 inner)를 "정의했을 때"의 자신을 포함한 스코프 정보를 기억하고 있는 것이다.  실제로 기억하고 있는가를 확인하기 위해 위와 같이 func_closure 프로퍼티를 호출하여 확인할 수 있다.

 "정의했을 때"라고 말한 것을 기억해두길 바란다. inner함수는 outer함수가 호출 될 때마다 새롭게 정의된다.

>>> def outer(x):
...     def inner():
...         print x
...     return inner
>>> print1 = outer(1)
>>> print2 = outer(2)
>>> print1()
1
>>> print2()
2

 위의 예에서는 print1이나 print2에 직접 값을 인수로써 넣지 않아도 각각의 내부의 inner함수가 어떤 값을 출력해야하나를 기억하고 있다. 이것을 이용해서 고정 인수를 얻도록 커스터마이즈한 함수를 생성하는 것도 가능하다.

 

 

Step9. 데코레이터


 드디어 데코레이터에 관련된 이야기를 할 때가 왔다. 여기까지의 이야기를 바탕으로 결론부터 말하자면, 데코레이터란 "함수를 인수로 얻고 대가로 새로운 함수로 돌려주는 cllable(*)"이다.

>>> def outer(some_func):
...     def inner():
...         print "before some_func"
...         ret = some_func() #1
...         return ret + 1
...     return inner
>>> def foo():
...     return 1
>>> decorated = outer(foo) #2
>>> decorated()
before some_func
2

 하나 하나 이해해보도록 하자. 여기서 파라미터로써 some_func를 취득하는 outer이라는 함수를 정의하고 있다. 여기서 outer 안에 inner이라는 내부 함수가 정의되어 있다.

 inner은 문자열을 print한 후에, #1을 반환하는 값을 취득하고 있다. some_func는 outer를 호출하는 것으로 다른 값을 얻을 수 있지만, 여기서는 그것이 무엇이 되었든 아무튼 일단 실행 (call)하고 그 결과에 1을 더한 값을 반환한다. 마지막으로 outer함수는 inner함수의 것을 반환한다.

 #2에서 some_func로써 foo를 outer를 실행한 리턴값을 변수 decorated에 저장하고 있다. foo를 실행하면 1이 반환되지만 outer에을 씌운 foo는 그것에 +1을 하여 2를 반환하고 있다. 말하자면 decorated는 foo의 데코레이터판(foo + 어떠한 처리)라고 할 수 있다.

 실제로 유용한 데코레이터를 사용할 때에는 decorated와 같이 다른 변수를 준비할 필요없이 foo에 그대로 대입하는 경우가 많다. 즉 아래와 같다는 것이다.

foo = outer(foo)

 또 이전의 foo는 부르지 않고, 보통 데코레이트된 foo가 반환되는 것이 된다. 실용적인 예를 살펴보자.

 어떠한 좌표의 객체를 유지하는 라이브러리를 사용하고자한다. 이 객체는 x와 y의 좌표 쌍을 가지고 있지만, 아쉽게도 덧셈이나 나눗셈 등 수학 처리 기능은 없다. 그러나 우리가 이 라이브러리를 이용해서 대량의 계산 처리를 할 필요가 있어 라이브러리의 소스를 개편해야하는 반갑지 않은 상황이다. 

 우선 접근법으로는 아래와 같이 add, sub와 같은 함수를 만들면 좋을 것이라고 생각한다.

>>> class Coordinate(object):
...     def __init__(self, x, y):
...         self.x = x
...         self.y = y
...     def __repr__(self):
...         return "Coord: " + str(self.__dict__)
>>> def add(a, b):
...     return Coordinate(a.x + b.x, a.y + b.y)
>>> def sub(a, b):
...     return Coordinate(a.x - b.x, a.y - b.y)
>>> one = Coordinate(100, 200)
>>> two = Coordinate(300, 200)
>>> add(one, two)
Coord: {'y': 400, 'x': 400}

 여기서 예를 들어 [취급할 좌표계는 0이하일 필요가 있다]라는 체크 처리가 필요하다면 어떻게 할 수 있을까? 즉 아래와 같은 상황이 발생을 막아야한다면 어떻게 코드를 작성할 수 있을까?

>>> one = Coordinate(100, 200)
>>> two = Coordinate(300, 200)
>>> three = Coordinate(-100, -100)
>>> sub(one, two)
Coord: {'y': 0, 'x': -200}
>>> add(one, three)
Coord: {'y': 100, 'x': 0}

 sub(one, two)는 (0, 0)을, add(one, three)는 (100, 200)을 반환하길 바라는 것이다. 각각의 함수에 하한을 체크하는 처리를 작성하는 것을 생각해볼 수 있겠지만, 여기서 데코레이터를 사용하여 체크 처리를 한 번에 처리하도록 만들어보자.

>>> def wrapper(func):
...     def checker(a, b):
...         if a.x < 0 or a.y < 0:
...             a = Coordinate(a.x if a.x > 0 else 0, a.y if a.y > 0 else 0)
...         if b.x < 0 or b.y < 0:
...             b = Coordinate(b.x if b.x > 0 else 0, b.y if b.y > 0 else 0)
...         ret = func(a, b)
...         if ret.x < 0 or ret.y < 0:
...             ret = Coordinate(ret.x if ret.x > 0 else 0, ret.y if ret.y > 0 else 0)
...         return ret
...     return checker
>>> add = wrapper(add)
>>> sub = wrapper(sub)
>>> sub(one, two)
Coord: {'y': 0, 'x': 0}
>>> add(one, three)
Coord: {'y': 200, 'x': 100}

 이전의 foo = outer(foo) 부분은 동일하지만 유용한 체크 구조를 파라미터와 함수의 처리 결과에 대해 적용할 수 있다. 

 

Step10. @심볼의 적용


Python에서는 데코레이터의 기재에 관해서 @기호를 사용한다. 즉 아래의 코드를

>>> add = wrapper(add)

이와 같은 형식으로 작성할 수 있다.

>>> @wrapper
... def add(a, b):
...     return Coordinate(a.x + b.x, a.y + b.y)

여기까지 읽어봤을 때, classmethod나 staticmethod와 같이 유용한 데코레이터를 자신이 만드는 것은 꽤 레벨이 높지만 적어도 데코레이터를 사용하는 것은 그렇게 어렵지 않다. 단지 @decoratorname를 기재할 뿐이다. 

 

Step11. *args와 **kwargs


 바로 위에서 설명한 데코레이터 wrapper은 유용하지만 파라미터가 2개뿐인 함수에만 적용할 수 있다. 물론 그건 그것대로 괜찮지만 더욱 유연한 함수에 적용할 수 있도록 데코레이터를 작성하고 싶은 경우는 어떻게 하는 것이 좋을까?

 Pytho에는 이것을 지원하는 기능이 준비되어 있다. 상세한 내용을 공식 사이트의 문서를 읽으면 좋겠지만, 함수를 정의할 때는 파라미터에 *(아스터리스크)를 붙이면 임의의 수의 필수 파라미터를 수용하도록 할 수 있다.

>>> def one(*args):
...     print args
>>> one()
()
>>> one(1, 2, 3)
(1, 2, 3)
>>> def two(x, y, *args):
...     print x, y, args
>>> two('a', 'b', 'c')
a b ('c',)

 임의의 파라미터 부분과 관련해서 리스트를 전달하고 있다는 것을 알 수 있다. 또한 정의할 때뿐만 아니라 호출할 때에도 인수에 *를 붙이면 기존 리스트나 튜플 형식의 인수를 언패키징해서 고종 인수에 적용해준다(아래의 코드를 참고). 

>>> def add(x, y):
...     return x + y
>>> lst = [1,2]
>>> add(lst[0], lst[1]) #1
3
>>> add(*lst) #2 <- # #1과 완전히 같은 의미이다.
3

 또한 **(애스터리스크 2개) 기법도 존재한다. 여기서는 리스트가 아닌 사전형이 된다.

>>> def foo(**kwargs):
...     print kwargs
>>> foo()
{}
>>> foo(x=1, y=2)
{'y': 2, 'x': 1}

 **kwargs를 함수 정의에 사용하는 것은 "명시적으로 지정하지 않은 파라미터는 kwargs이라는 이름의 사전으로 저장된다"는 의미이다. *args와 동일하게 함수를 호출할 때의 언패키징에도 대응한다.

>>> dct = {'x': 1, 'y': 2}
>>> def bar(x, y):
...     return x + y
>>> bar(**dct)
3

 

 

Step12. 제네릭한 데코레이터


위의 기능을 이용하여 함수의 인수를 로그에 출력해주는 데코레이터를 작성해보자. 간략화하기 위해 로그 출력은 stdout에 print하자.

>>> def logger(func):
...     def inner(*args, **kwargs): #1
...         print "Arguments were: %s, %s" % (args, kwargs)
...         return func(*args, **kwargs) #2
...     return inner

 #1에서 inner함수는 임의의 개수, 형식의 파라미터를 취득하는 것이 가능하여, #2에서 그것을 언패키징하여 인수로써 전달할 수 있다는 것을 주목하자. 이것에 의해 어떠한 함수에 대해서도 데코레이터 logger을 적용할 수 있다.

>>> @logger
... def foo1(x, y=1):
...     return x * y
>>> @logger
... def foo2():
...     return 2
>>> foo1(5, 4)
Arguments were: (5, 4), {}
20
>>> foo1(1)
Arguments were: (1,), {}
1
>>> foo2()
Arguments were: (), {}
2

 

 

더욱 자세히 알아보고 싶은사람을 위해


 마지막의 예까지 이해했다면, 데코레이터의 기초에 대해 이해했다고 할 수 있다. 잘 모르겠다면 그 전 단계로 돌아가 다시 하나 하나 차분히 살펴보길 바란다. 혹시 데코레이터에 대해서 더욱 학습하고 싶은 경우 아래의 두 사이트를 추천한다. (영어) 

Decorators I: Introduction to Python Decorators

Python Decorators II: Decorator Arguments



출처: https://engineer-mole.tistory.com/181 [매일 꾸준히, 더 깊이]

 

일반 데코레이터

  1. 이렇게 코딩하고 실행하면
    # 얘가 데코레이터
    def decorator(func):
        def decorator(*args, **kwargs):
            print("%s %s" % (func.__name__, "before"))
            result = func(*args, **kwargs)
            print("%s %s" % (func.__name__ , "after"))
            return result
        return decorator
    
    # 함수에 데코레이터를 붙여준다.
    @decorator
    def func(x, y):
        print(x + y)
        return x + y
    
    func(1,2)
  2. 이런 결과가 나온다. 아! 신통방통 하다!
    func before
    3
    func after
  3. @데코레이터는 사실 이거랑 같은 의미라고 한다
    def decorator(func):
        def decorator(*args, **kwargs):
            print("%s %s" % (func.__name__, "before"))
            result = func(*args, **kwargs)
            print("%s %s" % (func.__name__ , "after"))
            return result
        return decorator
    
    def func(x, y):
        print(x + y)
        return x + y
    
    func2 = decorator(func)
    func2(1,2)

파라메터를 가지는 데코레이터

  1. 데코레이터에 뭔가 파라메터를 전달하고 싶을데는 약간 복잡하긴 하지만 역시 다 된다! function을 감싸는 decorator를 다시 감싸주면 된다.
    # 얘가 파라메터도 붙는 데코레이터
    def decorator_with_param(param):
        def wrapper(func):
            def decorator(*args, **kwargs):
                print(param)
                print("%s %s" % (func.__name__, "before"))
                result = func(*args, **kwargs)
                print("%s %s" % (func.__name__ , "after"))
                return result
            return decorator
        return wrapper
    
    @decorator_with_param("hello, decorator!")
    def func(x, y):
        print(x + y)
        return x + y
    
    func(1,2)
  2. 결과는 이렇게 나온다!
    hello, decorator!
    func before
    3
    func after

-------

본질적으로, decorators는 wrapper로써 동작하며, 원본 함수를 수정할 필요 없이 원본 함수의 실행 전/후의 동작을 변경할 수 있습니다

 

함수에 대해 알아야 할 것

깊게 들어가기 전에, 명확히 알아야 할 전제조건이 있습니다. python에서 함수는 일급시민이며, 일급객체입니다. 이 말은 아래에 나오는 여러 유용한 작업들을 할 수 있다는 의미입니다.

함수를 변수에 대입할 수 있다.

def greet(name):
    return "hello "+name

greet_someone = greet
print greet_someone("John")

# Outputs: hello John

함수 내부에 다른 함수를 정의할 수 있다.

def greet(name):
    def get_message():
        return "Hello "

    result = get_message()+name
    return result

print(greet("John"))

## 실행결과 
Hello John

함수는 다른 함수의 인자로 전달될 수 있다.

def greet(name):
   return "Hello " + name 

def call_func(func):
    other_name = "John"
    return func(other_name)  

print(call_func(greet))

## 실행결과
Hello John

함수는 다른 함수에 의해 리턴될 수 있다.

다른 말로 함수는 다른 함수를 생성 할 수 있습니다.

def compose_greet_func():
    def get_message():
        return "Hello there!"

    return get_message

greet = compose_greet_func()
print(greet())

## 실행결과
Hello there!

내부 함수는 둘러싸인 범위(enclosing scope)에 대해 접근이 가능하다.

보통 closure로 알려져 있습니다. 내부 함수의 근접한 scope로부터 name 인자를 읽기 위해 위의 예제로부터 어떻게 변경했는지 봅시다.

def compose_greet_func(name):
    def get_message():
        return "Hello there "+name+"!"

    return get_message

greet = compose_greet_func("John")
print(greet())

## 실행결과 
Hello there John!

Decorators의 구성

함수 decorators는 존재하는 함수에 대한 간단한 wrappers입니다. 위에서 언급한 아이디어를 모아서 decorators를 만들 수 있습니다. 아래 예제에서, p 태그에 의해 문자열을 출력하는 함수를 wrap 하는 것을 고려해보겠습니다.

def get_text(name):
   return "lorem ipsum, {0} dolor sit amet".format(name)

def p_decorate(func):
   def func_wrapper(name):
       return "<p>{0}</p>".format(func(name))
   return func_wrapper

my_get_text = p_decorate(get_text)

print my_get_text("John")

# <p>Outputs lorem ipsum, John dolor sit amet</p>

이게 우리의 첫 번째 decorator 입니다. 함수는 다른 함수를 인자로 전달 받고 기존 함수의 기능에 추가적인 작업을 하는 새로운 함수를 생성하고 리턴합니다. get_text 함수를 p_decorate로 꾸미고 싶다면 우린 단지 p_decorate의 리턴 값을 get_text에 할당하면 됩니다.

get_text = p_decorate(get_text)

print get_text("John")

# Outputs lorem ipsum, John dolor sit amet

Python’s Decorator 문법

python은 문법적인 sugar(컴퓨터 용어 sugar란 기존 기능을 좀 더 쉽게 쓰기 위한 문법적 방법)를 통해 programmer들에게 좀 더 명확하고 보기 좋은 decorator 방법을 제공합니다. get_text를 decorate 하기 위해 get_text = p_decorator(get_text)와 같은 코드를 쓸 필요가 없습니다. decorated 된 함수 전에 decoration 하는 함수의 이름을 언급하는 방법이 있습니다. decorator 이름과 함께 @ 심볼이 나와야 합니다.

def p_decorate(func):
   def func_wrapper(name):
       return "<p>{0}</p>".format(func(name))
   return func_wrapper

@p_decorate
def get_text(name):
   return "lorem ipsum, {0} dolor sit amet".format(name)

print get_text("John")

# Outputs <p>lorem ipsum, John dolor sit amet</p>

이제 우리는 get_text 함수를 div와 string 태그로 감싸는 두 개의 다른 함수를 고려해봅시다.

def p_decorate(func):
   def func_wrapper(name):
       return "<p>{0}</p>".format(func(name))
   return func_wrapper

def strong_decorate(func):
    def func_wrapper(name):
        return "<strong>{0}</strong>".format(func(name))
    return func_wrapper

def div_decorate(func):
    def func_wrapper(name):
        return "<div>{0}</div>".format(func(name))
    return func_wrapper

기본적인 접근방법으론, 아래 코드와 같이 decorating 할 수 있습니다.

get_text = div_decorate(p_decorate(strong_decorate(get_text)))

Python의 decorator 문법으로 동일한 기능을 제공합니다.

@div_decorate
@p_decorate
@strong_decorate
def get_text(name):
   return "lorem ipsum, {0} dolor sit amet".format(name)

print get_text("John")

# Outputs <div><p><strong>lorem ipsum, John dolor sit amet</strong></p></div>

여기서 한가지 중요한 것은 decorators의 순서입니다. 만약 위의 예제에서 decorators의 순서가 달라지면 결과도 달라집니다.

Class의 Method Decorating

Python에서 methods는 첫 번째 인자가 객체를 참조(self)하는 함수입니다. wrapper 함수의 인자를 self를 받는 방법으로 동일하게 methods에 대해 decorators를 만들 수 있습니다.

def p_decorate(func):
   def func_wrapper(self):
       return "<p>{0}</p>".format(func(self))
   return func_wrapper

class Person(object):
    def __init__(self):
        self.name = "John"
        self.family = "Doe"

    @p_decorate
    def get_fullname(self):
        return self.name+" "+self.family

my_person = Person()
print my_person.get_fullname()

더 좋은 접근은 우리의 decorator가 함수와 methods 모두에게 유용하도록 만드는 것입니다. 이 방법을 위해 wrapper 함수의 인자로 args 와 *kwargs를 넣고 이를 통해 가변인자와 가변 키워드 인자를 전달받을 수 있습니다.

def p_decorate(func):
   def func_wrapper(*args, **kwargs):
       return "<p>{0}</p>".format(func(*args, **kwargs))
   return func_wrapper

class Person(object):
    def __init__(self):
        self.name = "John"
        self.family = "Doe"

    @p_decorate
    def get_fullname(self):
        return self.name+" "+self.family

my_person = Person()

print my_person.get_fullname()

decorators에게 인자 전달하기

위의 예제들을 돌아보면, 많은 decorators 예제들이 중복되어 있는 것을 알 수 있습니다. 3개의 decorators( div_decorate, p_decorate, strong_decorate)를 살펴보면 name 변수의 string을 서로 다른 태그로 감싸는 것만 다르고 다른 동작은 모두 동일합니다. 우리는 명백하게 더 많은 것을 할 수 있어야만 합니다. tag를 인자로 받아 string을 감싸는 일반적인 구현방법은 없을까요? 제발염!

def tags(tag_name):
    def tags_decorator(func):
        def func_wrapper(name):
            return "<{0}>{1}</{0}>".format(tag_name, func(name))
        return func_wrapper
    return tags_decorator

@tags("p")
def get_text(name):
    return "Hello "+name

print get_text("John")

# Outputs <p>Hello John</p>

위 경우 조금 더 많은 작업을 했습니다. Decorators는 인자로 함수를 받기를 기대하기 때문에, 우리는 추가적인 인자를 받아 우리의 decorator를 즉석에서 만드는 함수를 정의했습니다. 위의 예제에선 tags로 우리의 decorator를 생성합니다.

decorated 된 함수 디버깅

결국 가장 중요한 것은 decorators는 단지 우리가 만든 함수를 wrapping 한다는 것입니다. 이 파트에선, wrapper 함수가 원본 함수의 name, module, docstring을 가지지 않기 때문에 발생하는 문제를 다룹니다. 위의 예제들을 기반으로 할 경우 :

print get_text.__name__
# Outputs func_wrapper

출력은 get_text로 기대했지만, get_text의 __name__, __doc__, __module__의 속성이 이런 warpper 함수들에 의해 오버라이딩 됩니다. 분명하게 우리는 wrapper 함수 안에서 이들을 reset 해줘야 하고, python은 훨씬 좋은 방법을 제공합니다.

Functools 이 대안입니다.

운좋게도 python (version 2.5)는 functools.wraps를 포함하는 functools 모듈을 가지고 있습니다. Wraps는 wrapping 하는 함수의 속성들을 원본 함수의 속성들로 update 해주는 decorator 입니다. 아래 예제가 있습니다.

from functools import wraps

def tags(tag_name):
    def tags_decorator(func):
        @wraps(func)
        def func_wrapper(name):
            return "<{0}>{1}</{0}>".format(tag_name, func(name))
        return func_wrapper
    return tags_decorator

@tags("p")
def get_text(name):
    """returns some text"""
    return "Hello "+name

print get_text.__name__ # get_text
print get_text.__doc__ # returns some text
print get_text.__module__ # __main__

이제 get_text의 속성이 제대로 나오는 것을 알 수 있습니다.

decorators는 어디에 쓰나?

decorators 는 우리의 프로그램을 더욱 더 강력하고 우아하게 만들어줍니다. 일반적으로 decorators는 우리가 함수의 기능변경을 원치 않는 상황에서 기능을 확장하고 싶을 때 사용하는 개념입니다.

'Python' 카테고리의 다른 글

Python - module  (0) 2022.01.06
Python - class  (0) 2022.01.06
파이썬 - list  (0) 2022.01.05
파이썬 - lambda 함수  (0) 2022.01.05
Python 코드를 실행하는 Visual Studio Code에서 code runner 메시지를 숨기는 방법  (0) 2022.01.04

1. List란?

파이썬의 자료구조 형태중 하나로 리스트는 값을 나열하는것 이라고 정의할 수 있겠습니다.
또한 List는 시퀀스데이터 이기도 합니다.
 
시퀀스는 데이터의 순서를 정하는것을 의미합니다.
순서를 가지고 있기 때문에 인덱스(index)를 사용하여 참조할 수 있습니다.
인덱스란 문자열이 있으면 번호를 뜻합니다.
Ex)문자열 인덱싱
a = 'hello' 문자열로 했을때, a[0]을 확인하면 첫번째 문자열인 h가 출력됩니다. 이것을 문자열 인덱싱이라 하며, 시퀀스자료형이기 때문에 가능한 것이죠.
 
 
파이썬에서의 시퀀스 자료형은?(순서를 정함)
string, list, tuple
 
시퀀스 자료형이 아닌것은?(순서를 정하지 않음)
set, dict

 

 

1.1 List 만들어보기

1
list_name = [1,2,3,4,5,6]
cs

이런식으로 리스트이름 = [] 대괄호 안에 숫자나 문자등을 넣어서 만들 수 있습니다.

 

1
2
3
list_name_num = [1,2,3,4,5,6]
list_name_string = ['h','i']
list_name_word = ['hello']
cs

이렇게 문자도 가능합니다.

 

1.2 List 인덱싱

1.2.1 인덱싱의 기초

앞서 말했듯 List는 파이썬의 시퀀스 자료형 이기 때문에 인덱스(쉽게말해 자리 번호표)를 가지고 있습니다.
그것을 가지고 연산을 할수 있는데 예제를 통해 알아보겠습니다.
 
 
1
2
= [1,2,3]
print(a[0])
cs

결과는 어떻게 나올까요?

a라는 [리스트] 안에 0번째 인덱스를 프린트 했을때 무엇이 나올까요?

답은 1 입니다.

 

실험. 만약에 없는 인덱스를 프린트 했을경우 어떻게 될까?

a[3]은 없는 인덱스이기 때문에 당연히 index out of range라는 에러가 나오게 됩니다.

 

1.2.2 List 인덱싱의 연산

이요소들을 합치면 어떤 반응이 일어날까요?
1
2
= [1,2,3]
print(a[0]+a[2])
cs

의 답은 무엇일까요?

a[0]은 1

a[2]는 3

즉 1+3이 되어 4가 나오게 될것입니다.

 

곱하기도 될까요?

1
2
= [1,2,3]
print(a[0]*a[2])
cs

네 물론 됩니다.

a[0]은 1

a[2]는 3

즉 1*3이 되어 3가 나오게 될것입니다.

 

그럼 빼기도 되겠죠?

1
2
= [1,2,3]
print(a[0]-a[2])
cs

네 물론 됩니다.

a[0]은 1

a[2]는 3

즉 1-3이 되어 -2가 나오게 될것입니다.

 

 

문자는 더하거나 곱하거나 빼면 어떻게 될까요?

1
2
= ['h','e','ll','o']
print(a[0]+a[2])
cs
답: hll

 

그외 곱하기와 빼기는 문자열 이기 때문에 안된다는 에러메시지가 나옵니다.

 

1.3 리스트 슬라이싱

리스트의 슬라이싱은 매우 간단합니다.
리스트의 요소들이 있다면 그중 원하는 부분만 잘라내서 가져오는 것으로 생각하시면 되는데 실습을 통해서 알아보겠습니다.
 
기본 문법은 출력시에 name[:] 이며
[num:] num 이상(포함하고 부터)
[:num] num 미만(포함하지 않고 까지)
[num1:num2] num1이상 ~ num2미만
[::-1] 거꾸로
[1:10:2] 1이상 10미만중 2개씩만
 
예제를 통해 알아보겠습니다.
 
1
2
3
4
5
6
7
8
9
number = [1,2,3,4,5,6,7,8,9,10]
 
print(number[1:3])#이상~미만
print(number[3:])#이상
print(number[:3])#미만
print(number[1:3:2])#1이상 3미만 2개씩
print(number[0:9:2])#0이상 9미만 2개씩
print(number[::-1])#거꾸로
 
cs

주의하실점은 num:num일때 이상~미만입니다.

1:3일때 1번째를 포함하는애 부터 3번째는 포함하지 않습니다

이 점만 주의하시면 슬라이싱하시는데 문제 없으실겁니다.

 

 

1.4 리스트 연산

1.4.1 리스트 더하기

너무나도 간단하기 때문에 예제만 봐도 충분이 이해가 가실겁니다.

그냥 리스트끼리 합쳐져서 순서대로 출력됩니다.

 

 

1.4.2 리스트 반복

리스트의 기초는 너무 쉬워요~! 그냥 리스트*숫자를 사용하면 숫자만크 리스트를 반복시킵니다

 

 

1.4.3 리스트 수정

 

1.4.4 리스트 삭제

1.4.5 리스트 요소 추가

리스트의 요소를 추가하는 방법은 간단합니다.
리스트.append(요소) 를 사용하시면 되는데 주의하실점은 한번에 두개의 요소가 추가되지는 않지만
리스트안에 리스트를 추가할 수는 있다는 점입니다.

여러개의 리스트요소를 추가하고 싶을때 맨아래 extend를 참고하시면 됩니다.

 

1.4.6 리스트 정렬(sort)

문자와 숫자를 함께 정렬할수는 없는 것을 확인하실 수 있습니다.

 

 

1.4.7 리스트 뒤집기(reverse)

숫자와 문자가 함께있어도 잘 뒤집힙니다.

 

 

1.4.7 리스트 위치 반환(index)

a안에 있는 3의 요소 index를 확인하고 싶을때

a안에 있는 1의 요소 index를 확인하고 싶을때

없는 요소를 확인하면 list에는 요소가 없다고 에러가 납니다.

 

 

1.4.8 리스트 요소 삽입(insert)

3번째 index에 4를 넣겠다는 뜻입니다.

a = [ 1,  2,  3 ]

[0]  [1] [2] index 이기 때문에 3번째에 4가 추가되어 [1,2,3,4]가 됩니다.

 

 

1.4.9 리스트 요소 제거(remove)

여러개의 요소가 있다면 맨앞에 한개의 요소만 제거합니다.

 

 

1.4.10 리스트 요소 꺼내기(pop)

가장 뒤에 있는 요소가 삭제됩니다.

 

 

1.4.11 리스트 요소 갯수 세기(count)

요소의 갯수를 카운트합니다.

 

 

1.4.12 리스트 확장(extend)

list안에 요소를 추가하는데 추가개념이 아닌 확장 개념이기 때문에 결과값이 다음과 같이 나오게 된것입니다. 



출처: https://rednooby.tistory.com/109 [개발자의 취미생활]

'Python' 카테고리의 다른 글

Python - class  (0) 2022.01.06
python - decorator  (0) 2022.01.05
파이썬 - lambda 함수  (0) 2022.01.05
Python 코드를 실행하는 Visual Studio Code에서 code runner 메시지를 숨기는 방법  (0) 2022.01.04
파이썬 시작하기  (0) 2022.01.03

lambda는 왜 쓰는가?

  • 익명함수이기 때문에 한번 쓰이고 다음줄로 넘어가면 힙(heap) 메모리 영역에서 증발
  • (참고) 가비지 컬렉터 (참조하는 객체가 없으면 지워버린다)
    • 파이썬에서는 모든것이 객체로 관리 되고 각 객체들은 레퍼런스 카운터를 갖게 됩니다. 이 카운터가 0 즉, 그 누구도 참조하지 않게 된다면 메모리를 환원 하게 됩니다.

 

람다함수는 적절한 곳에 잘 쓰인다면 코드가 간결해 보이지만, 그렇지 않고 남용하게 된다면 코드가 코드 가독성이 떨어질 수 있습니다. 

<목차>
1. lambda 함수 설명
2. lambda 함수와 map
3. lambda 함수와 filter

4. lambda 함수와 reduce

 

1. 파이썬 람다 함수 설명


원래 함수는 복잡한 명령들을 편하게 반복해서 사용할 수 있도록 모아두는 역할을 하는데, def 를 이용해서 다른곳에 함수를 만들고 그걸 또 호출해서 부르기까지의 수고가 필요하지 않은 그런 "일회성 함수"들을 위해서 만들어진게 람다 함수 입니다.

> 람다함수 선언 방법

lambda 인자: 표현식

lambda 라는 키워드를 입력하고 뒤에는 매개변수(인자)를 입력하고 콜론(:)을 넣은다음에 그 매개변수(인자)를 이용한 동작들을 적으면 됩니다.
예를 들면 인자로 들어온 값에 2를 곱해서 반환한다고 하면 lambda x : x * 2  이런식으로 됩니다.

> 람다함수와 일반함수 비교

위에서 설명했듯이 람다함수는 일반함수를 가볍게 만든 함수이기 때문에 
짝수를 판별하는 함수를 만든다고 했을때 일반함수는
def is_even(x):
  return x % 2 == 0
이런식으로 표현을 할 수 있습니다.

이걸 람다로 표현하게 되면
is_even = lambda x : x % 2 == 0
이런식으로 표현할수 있습니다.

물론 일반함수, 람다함수 둘다 사용법은 같습니다.

 


is_even(1) # False
is_even(2) # True

 

2. 파이썬 lambda 함수와 map


map함수는 리스트나 튜플에 어떤 특별한 처리를 할때 사용하는 함수입니다.

> map 함수의 모양과 사용법

map(함수, 리스트나 튜플)

첫번째 인자인 함수는 두번째 인자로 들어온 리스트나, 튜플에 특별한 가공 처리를 하는 함수이며, 사용자가 직접 함수를 정의해서 넣습니다.
두번째 인자인 리스트나 튜플은 바꾸고자 하는 데이터들을 집어 넣습니다.

예를들어 리스트의 모든값에 2씩 더해서 새로운 리스트를 만든다고 할때  map 함수는 아래와 같이 사용할 수 있습니다.
map 의 반환값을 리스트로 다시 변경할수 있는데요 이때는 list(map(~~~)) 이런식으로 list 로 형변환해주는 함수로 감싸주면 됩니다.

# 1. 일반 함수 버전
def plus_two(x):
    return x + 2

result1 = list(map(plus_two, [1, 2, 3, 4, 5]))
print(result1)

## 실행결과
[3, 4, 5, 6, 7]

# 2.  람다 함수 버전
result2 = list(map((lambda x: x + 2), [1, 2, 3, 4, 5]))
print(result2)

## 실행결과
[3, 4, 5, 6, 7]
# 1. 일반 함수 버전
def plus_two(x):
    return x + 2

result1 = list(map(plus_two, [1, 2, 3, 4, 5]))
print(result1)


# 2.  람다 함수 버전
result2 = list(map((lambda x: x + 2), [1, 2, 3, 4, 5]))
print(result2)

 

이런식으로 def를 통해서 함수를 map의 인자로 전달해줘도 되지만,
map을 통해서 "리스트를 가공하는게 이번 한번 뿐이다" 라는 상황이라던지 lambda를 사용해서 간단하게 작업이 가능한 경우라면
람다 함수 사용을 고려해보는 것도 좋을듯 합니다.

여기서 range를 또 결합하면 
result3 = list(map((lambda x : x + 2), range(2, 6))) 이런식으로도 쓸 수 있습니다.

result3 = list(map((lambda x : x + 2), range(2, 6)))
print(result3)

-- 실행결과
[4, 5, 6, 7]

이렇게 람다 함수는 map 함수와 같이 사용자 정의 함수를 받을때 유용하게 작성할 수 있습니다.

 

3. python 람다 함수와 filter


filter 함수는 map 함수와 비슷하게  첫번쨰 인자로 함수를 받고, 두번째 인자로 리스트나 튜플을 받습니다.

> filter 함수 모양과 설명

filter(함수, 리스트나 튜플)

첫번째 인자에는 두번째 인자로 들어온 리스트나 튜플을 하나하나씩 받아서 필터링할 함수를 넣습니다.
두번쨰 인자에는 리스트나 튜플을 집어 넣습니다.

예를들어 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 까지의 리스트가 있을때 짝수들만 filter 함수를 이용해서 리스트를 다시 만든다고 했을때, filter 함수와 람다를 이용할 수 있습니다.

# 1. 일반 함수 버전
def is_even(x):
    return x % 2 == 0


result1 = list(filter(is_even, range(10)))  # [0 ~ 9]
print(result1)

## 실행결과
[0, 2, 4, 6, 8]

# 2.  람다 함수 버전
result2 = list(filter((lambda x: x % 2 == 0), range(10)))  # [0 ~ 9]
print(result2)

## 실행결과
[0, 2, 4, 6, 8]

 

결과 화면을 보앗듯 filter 함수의 첫번째 인자가 True 인 경우의 값만 가지고와서 리스트를 만들었습니다.
 filter 함수와 람다 함수를 결합하여 깔끔하게 filter가 가능합니다.

일회성 함수를 위해서 "def 함수 ~~" 이런작업을 해주지 않아도 됩니다.

4. python 람다 함수와 reduce


python3 부터 내장함수에서 빠졌습니다.  사용하려면 functools 모듈에서 reduce 함수를 가져와야 합니다

 

filter 함수는 map 함수와 비슷하게  첫번쨰 인자로 함수를 받고, 두번째 인자로 리스트나 튜플을 받습니다.

reduce() 함수는 두 개의 필수 인자와 하나의 옵션 인자를 가지는데, function 을 사용해서 iterable을 하나의 값으로 줄입니다. initializer 초기값을 의미합니다.
reduce() 함수를 사용할 때 많은 분들이 실수하는 부분이 있는데 바로 초기값 세팅 유무에 따라 이상한 결과가 나올 수 있는 것입니다.

예를 들어, 유저의 나이 합계를 구하는 예제에서 초기값으로 0을 넘겨주지 않으면 TypeError가 발생합니다.


functools 모듈의 reduce 함수는 다음과 같습니다. 

def reduce(function, iterable, initializer=None):
    it = iter(iterable)
    if initializer is None:
        value = next(it)
    else:
        value = initializer
    for element in it:
        value = function(value, element)
    return value

 

Syntax

reduce(함수, 리스트나 튜플[, initializer])

 

reduce()

reduce()은 리스트의 아이템들을 누적해서 하나의 값으로 리턴해주는 함수입니다. Lambda와 함께 사용할 수 있습니다.

reduce()의 첫번째 인자로 Lambda를 전달하고, 두번째 인자에 리스트를 전달합니다.

# 일반함수를 사용한 경우
from functools import reduce

def sum(x, y): 
    return x+y 
    
print(reduce(sum, range(10)))

print(lambda x,y : x+y, range(10)))

## 수행결과
45

# lambda를 사용한 경우
from functools import reduce

print(reduce(lambda x,y : x+y, range(10)))

## 수행결과
45

위에서 reduce의 연산 과정을 살펴보면, 처음에는 0 + 1이 수행되고, 그 결과를 다음 연산에 사용합니다. 그래서 그 다음에는 1 + 2이 수행되고, 3 + 36 + 4... 순서로 진행되어 마지막 값이 리턴됩니다. 따라서 결과는 0에서 9까지 합한 숫자가 리턴됩니다.

 

 

 

다음의 예제처럼 초기값을 10으로 주면, reduce 함수 코드에서 봤던 value 의 값에 10이 저장된 상태로 시작하기 때문에 10에 0부터 9까지의 합인 45를 더한 값이 리턴됩니다.

from functools import reduce

print(reduce(lambda x, y: x+y, range(10), 10))

## 실행결과
55

 

reduce 함수로 5 factorial 을 계산해 봅시다. 

from functools import reduce

print(reduce(lambda x, y: x*y, range(1, 5)))

## 실행결과
24


reduce 함수로 factorial 함수를 만들 수도 있습니다.

from functools import reduce

def factorial(n):
    return(reduce(lambda x, y: x*y, range(1, n+1)))

print(factorial(6))

## 실행결과
720

 

참고로, 파이썬은 다양한 수학적 연산을 지원하는 math 모듈을 내장하고 있는데 이 모듈에서 팩토리얼 함수를 제공합니다.
from math import factorial

print(factorial(6))

## 실행결과
720

'Python' 카테고리의 다른 글

python - decorator  (0) 2022.01.05
파이썬 - list  (0) 2022.01.05
Python 코드를 실행하는 Visual Studio Code에서 code runner 메시지를 숨기는 방법  (0) 2022.01.04
파이썬 시작하기  (0) 2022.01.03
[Python] VSCode argument 설정  (0) 2021.12.31

Python 코드를 실행하는 Visual Studio Code에서 code runner 메시지를 숨기는 방법

 

1. launch.json 파일에 "console": "externalTerminal"을 설정하면 외부 콘솔에 표시되고 스크립트가 표시되지 않습니다.

print('Hello, world')

2. launch.json 파일에 "console": "internalConsole"을 설정하면 결과가 DEBUG CONSOLE 표시되고 code runner 메시지가 표시되지 않습니다.

print('Hello, world')

 

3. Code Runner extension을 설치하고 settings.json에 아래와 같이 파라미터를 추가합니다.

Windows의 경우 setting.json 파일은 %AppData%/Roaming/Code/User/settings.json 위치에 있습니다.

Code Runner Extension
# settings.json 파일 위치
%AppData%/Roaming/Code/User/settings.json

# settings.json 파일에 아래 파라미터 추가
"code-runner.clearPreviousOutput": true,
"code-runner.showExecutionMessage": false,
 

위의 코드를 추가한 다음 단축키(Ctrl + Alt + N)을 누른거나, 마우스 오른쪽 버튼을 클릭한 후 [Run Code] 메뉴를 클릭합니다. 또는 VSCode 우측 상단의 실행버튼(▷)을 클릭해도 됩니다.

print('Hello, world')
 
 

추가

'Python' 카테고리의 다른 글

파이썬 - list  (0) 2022.01.05
파이썬 - lambda 함수  (0) 2022.01.05
파이썬 시작하기  (0) 2022.01.03
[Python] VSCode argument 설정  (0) 2021.12.31
Python과 SQLite DB 연동하기 - 수정  (0) 2021.12.31

Python은 Perl, Ruby, Scheme 또는 Java와 비교할 만한 명확하고 강력한

객체 지향 프로그래밍 언어입니다.

파이썬 특징

작성한 프로그램을 읽고 사용하기 쉬운 언어입니다. 
웹 서버 연결, 정규 표현식으로 텍스트 검색, 파일 읽기 및 수정과 같은 프로그래밍 작업을 지원하는 표준 라이브러리를 제공합니다.
Python의 대화형 모드를 사용하면 간단한 코드를 쉽게 테스트할 수 있습니다. IDLE이라는 번들 개발 환경도 제공합니다.
C 또는 C++와 같은 컴파일된 언어로 구현된 모듈을 추가하여 쉽게 확장할 수 있습니다.
프로그래밍 가능한 인터페이스를 제공하기 위해 애플리케이션에 내장될 수도 있습니다.
Mac OS X, Windows, Linux 및 Unix를 포함한 모든 곳에서 실행되며 Android 및 iOS용 빌드도 사용할 수 있습니다.

파이썬은 무료 소프트웨어입니다. Python을 사용하거나 애플리케이션에 포함하는 데 비용이 들지 않습니다. Python은 언어에 저작권이 있지만 오픈 소스 라이선스에 따라 자유롭게 수정 및 재배포할 수 있습니다.

 

Python의 프로그래밍 언어 기능은 다음과 같습니다.

숫자(부동 소수점, 복소수, 길이에 제한이 없는 긴 정수), 문자열(ASCII 및 유니코드), 목록 및 사전과 같은 다양한 기본 데이터 유형을 사용할 수 있습니다.

Python은 클래스 및 다중 상속을 통해 객체 지향 프로그래밍을 지원합니다.
코드는 모듈과 패키지로 그룹화할 수 있습니다.
예외 발생 처리를 지원하므로 보다 깔끔한 오류 처리가 가능합니다.
데이터 타입은 동적으로 지정됩니다. 호환되지 않는 타입을 결합하면(예: 문자열과 숫자 결합 시도) 예외가 발생하므로 오류를 빨리 확인할 수 있습니다.
Python에는 generators and list comprehensions와 같은 고급 프로그래밍 기능이 포함되어 있습니다.
Python의 자동 메모리 관리를 사용하면 코드에서 수동으로 메모리를 할당하고 해제할 필요가 없습니다.

 

시작하기

설치

파이썬 홈페이지에서 자신의 OS 환경에 맞는 프로그램을 다운로드 한 후 설치합니다.

본 문서의 모든 예제는 VS Code에서 실행했습니다. VS Code IDE 환경에서 개발하는 것을 권장합니다.

 

자료형

  • number(int, float, complex(복소수))
  • string
  • boolean : True, False
  • list : 자료 배열
  • tuple : 값을 변경할 수 없다.
  • dictionary : key : value 로 되어 있다.
  • set : unique하고 순서가 없다.
  • class

type(variable)로 자료형을 확인할 수 있다. Boolean형은 'True, False'인데 첫 문자가 반드시 대문자이어야 합니다.

# 자료형 확인 예제

s='abcd'
print(type(s))
## 수행결과
<class 'str'>

b=False 
type(b)
## 수행결과
<class 'bool'>

d = {'key1': 'value', 'key2': ['list', 'can', 'be', 'a', 'value']}
print(d)
## 수행결과
<class 'dict'>

st = set([1, 2, 2, 3, 4, 4, 4, 4])
type(st)
## 수행결과
<class 'set'>

 

자료형에 대한 상세 설명 추가 예정

LIST

 

input 설명

 

 

조건문

if 조건문

if 조건문은 if에 조건식을 지정하고 반드시 무장 끝에 :(콜론)을 붙이며 elif, else에도 문장 끝에 :(콜론)을 붙여야 합니다.

다음 줄에 실행할 코드를 기술하는데 반드시 들여쓰기를 해야 합니다. 들여쓰기의 길이는 한칸, 두칸, 네칸 등 칸 수는 상관없습니다.

if, if else, if elif else, if elif else 등으로 사용할 수 있습니다. elif 갯수는 제한없이 사용할 수 있습니다.

 

< if 문 형식>

# if 
if 조건식:
     코드

# if else
if 조건식:
     코드
else:
     코드

# if elif else
if 조건식:
     코드
elif 조건식:
     코드
else:
     코드

for 반복문과 함께 pass, continue, break를 사용할 수 있습니다.

  • pass : 실행할 코드가 없는 것으로 다음 코드를 계속해서 진행합니다.
  • continue : 다음 순번의 loop를 수행합니다.
  • break : 반복문을 멈추고 loop 밖으로 빠져 나가도록합니다.

 

반복문

파이썬 반복문에는 for 반복문과 while 반복문이 있습니다.

 

for 반복문

for 반복문에는 2가지 형태가 있는데 for in iterable과 for in range 형태가 있습니다.

 

for in iterable 에서 iterable 은 member를 하나씩 차례로 반환 가능한 object를 나타내고

string, list, dictionary 등이 올 수 있습니다.

 

<< for in iterable 형식 >>

# for in iterable (iterable에는 문자열, 리스트, 튜플, 딕셔너리, 집합 이 올 수 있음)
for 변수 in iterable[string | list | tuple | dictionary | set]:
    반복 수행부분

 

for in range(start, stop, [step]) 구문에서는

stop은 반드시 입력되어야 하고, start는 생략하면 기본값으로 0을, step은 생략하면 기본값으로 1을 설정됩니다. 

 

< < for in range 형식 >>

# for in range
for 변수 in range(start, stop, step):
    반복 수행부분

step 이 양수이면 start + i * step 이 stop 보다 작을 때까지,

step 이 음수이면 start + i * step 이 stop 보다 클 때까지 반복합니다.

쉽게 step이 1일 경우, stop -1 까지 수행한다고 생각하면 됩니다.

 

for 반복문 예제

iterable 중 list에 대한 반복문 예제입니다.

# list에 대한 for 반복문
var_list = [1, 3, 5, 7]
for i in var_list:
	print(i)

## 수행결과
1
3
5
7

 

아래는 for ... range 데 대한 예제입니다.

start와 step를 쓰지 않은 경우, start는 0 step 은 1이고 stop 은 5입니다.

stop 값이 5 이므로 0 + i * 1이 5보다 작을 때 까지 수행합니다. 여기서 i 는 start 값인 0 부터 시작합니다.

쉽게 시작값인 0 부터 5보다 1이 작은 4까지 수행되는 것입니다. 

아래 조건은 range(0, 5, 1) 과 동일합니다.

for i in range(5):
	print(i)

## 수행결과
0
1
2
3
4

1부터 10까지 더하는 예제

s = 0

for i in range(1, 11):
	s = s + i
print('1부터 10까지 합은 : ', s)

## 수행결과
1부터 10까지 합은 :  55

 

 

for loop with else 구문

for 루프는 선택적으로 else 구문도 쓸 수 있습니다.
for...else의 else 블록은 break 문의 존재 여부에 따라 다르게 실행됩니다.

1) for 루프에 break가 없으면 list의 모든 항목이 반복된 후 else 블록이 실행됩니다. 
names = ["alex", "brian", "charles"]

for x in names:
	print(x)
else:
	print("No name is left in the list")

## 수행결과
alex
brian
charles
No name is left in the list

 

 

2) for 루프에 break 문이 있는 경우:

– break 조건이 충족되지 않아 break가 실행되지 않은 경우 else 블록이 실행됩니다.

– break가 실행되는 경우 else 블록이 실행되지 않습니다. 

 
  • The break 가 실행되지 않은 경우
names = ["alex", "brian", "charles"]

for x in names:
	print(x)
	if x == "david":
		break;
else:
    print("No name is left in the list")

## 수행결과
alex
brian
charles
No name is left in the list
 
  • The break 문이 실행되는 경우
names = ["alex", "brian", "charles"]

for x in names:
	print(x)
	if x == "brian":
		break;
else:
	print("No name is left in the list")

## 수행결과
alex
brian

 

while 반복문

while문이란 조건식이 참(True)인 경우 실행문이 계속적으로 실행되는 파이썬 반복문입니다. for문과 동일하게 실행문이 반복 실행하지만, 그 반복횟수를 알 수 없는 경우 while문을 사용할 수 있습니다.

while과 조건식을 작성하고 마지막에 :(콜론)을 입력한 뒤, 다음 줄에 실행문을 작성하면 됩니다.

종료되지 않고 영원히 반복되는 while문을 무한 루프(Infinite loop)라고 합니다.

 

< while 문 형식 >

while [조건문]:
    [수행부분]

 

아래는 count가 0부터 5보다 작을 때까지 반복문을 실행하는 예제입니다.

count = 0

while count < 5:
    print(count, ' is less than 5')
    count = count + 1
else:
    print(count, ' is not less 5 so stop here')

## 실행결과
0  is less than 5
1  is less than 5
2  is less than 5
3  is less than 5
4  is less than 5
5  is not less 5 so stop here

 

 

while 반복문과 함께 pass, continue, break를 사용할 수 있습니다.

  • pass : 실행할 코드가 없는 것으로 다음 코드를 계속해서 진행합니다.
  • continue : 다음 순번의 loop를 수행합니다.
  • break : 반복문을 멈추고 loop 밖으로 빠져 나가도록합니다.

<<< break 예제 >>>

반복문 수행 중 break 문을 만나면 반복문을 빠져 나온다.

i = 0

while i < 6:
    i += 1
    if i == 3:
        break
        print('break', end=" ")
    print(i, end=" ")


## 실행결과
1 2

 

<< continue 예제 >>

조건에 부합하여 continue 명령어를 만나면 다음 순번의 반복문을 수행하게 한다.

즉, continue 를 만나면 다음 코드를 수행하지 않고 while 조건문을 체크한다.

i = 0

while i < 6:
    i += 1
    if i == 3:
        continue
        print('continue', end=" ")
    print(i, end=" ")

## 실행결과
1 2 4 5 6

 

<< pass 예제 >> 

조건에 부합하여 pass 명령어를 만나면 다음 코드를 수행한 후 다음 순번의 반복문을 수행하게 한다.

즉, pass를 만나면 다음 코드를 수행하고 while 조건문을 체크한다.

i = 0

while i < 6:
    i += 1
    if i == 3:
        pass
        print('pass', end=" ")
    print(i, end=" ")

## 실행결과
1 2 pass 3 4 5 6

 

while loop with else 구문

while 루프는 선택적으로 else 구문도 쓸 수 있습니다.
while ... else의 else 블록은 break 문의 존재 여부에 따라 다르게 실행됩니다.

 

위의 for 반복문에서 설명한 내용과 동일합니다.

 

무한 반복문 (infinite loop)

아래 예제는 사용자로부터 input()으로 문자를 받아서, 입력된 문자에 따라 프로그램을 계속 수행할지 중지할지를 하는 무한 반복문입니다. N 을 입력하지 않으면, 무한히 반복됩니다.

while True:
    print('계속하시겠습니까? [Y/N]:')
    answer = input()

    if answer == 'Y':
        print('>> 계속합니다.')
    elif answer == 'N':
        print('>> 중지합니다.')
        break
    else:
        print('>> 계속하려면 Y, 중지하려면 N 을 입력하세요')

## 실행 결과
계속하시겠습니까? [Y/N]:
예
>> 계속하려면 Y, 중지하려면 N 을 입력하세요
계속하시겠습니까? [Y/N]:
Y
>> 계속합니다.
계속하시겠습니까? [Y/N]:
N
>> 중지합니다.

 

<< HERE >>


함수

함수는 호출될 때만 실행되는 코드 블록입니다.
매개변수라고 하는 데이터를 함수에 전달할 수 있고 결과로 데이터를 반환할 수 있습니다. 

Python에서 함수는 def 키워드를 사용하여 정의하며 마직막에  :(콜론)을 붙여야 합니다.

예제) 함수생성

def my_function():
  print("Hello from a function")
 

함수 호출

함수를 호출하려면 함수 이름 뒤에 괄호를 사용합니다.

예제) 함수호출

def my_function():
  print("Hello from a function")

my_function()

-- 실행결과
Hello from a function
 

함수 인자(Arguments)

함수에는 인자를 전달할 수 있습니다.

인자는 함수 이름 뒤에 괄호 안에 지정됩니다. 원하는 만큼 인자를 추가할 수 있으며 쉼표로 구분하면 됩니다.

다음 예에는 하나의 인자(fname)가 있는 함수입니다. 함수가 호출되면 인자을 전달합니다.

전달된 인자는 함수 내부에서 사용됩니다.

예제) 함수인자

def my_function(fname):
  print(fname + " Refsnes")

my_function("Emil")
my_function("Tobias")
my_function("Linus")

-- 실행결과
Emil Refsnes
Tobias Refsnes
Linus Refsnes

인자(Arguments)는 종종 Python에서 args로 사용됩니다.

 

Parameters or Arguments?

매개변수와 인자라는 용어는 같은 용도로 사용할 수 있습니다. 즉, 함수에 전달되는 정보입니다.
함수의 관점에서:
매개변수(parameter)는 함수 정의에서 괄호 안에 정의된 변수입니다.
호출될 때 함수에 전송되는 값을 인자(argument)입니다.

아래는 파라미터와 인자(argument)를 설명한 예제입니다. 본 문서에서는 인수,인자,파라미터를 동일한 의미로 사용합니다.

# parameter (매개변수)
# 함수의 정의 부분에 나열되어 있는 변수, 여기서는 plus 함수 정의시에 사용되는 a, b를 parameter(매개변수) 라고 한다.
def plus(a, b):
  return a + b

# argument (전달인자)
# 함수를 호출할때 전달 되는 실제 값, 여기서는 plus 라는 함수에 넣어주는 값 1, 2를 argument(전달인자)라고 한다.
result = plus(1, 2)

Number of Arguments

기본적으로 함수는 정확한 수의 인자로 호출되어야 합니다. 즉, 함수에 2개의 인자가 필요한 경우 정확히 2개의 인자를 사용하여 함수를 호출해야 합니다.

예제)

## expects 2 arguments, and gets 2 arguments:

def my_function(fname, lname):
  print(fname + " " + lname)

my_function("길동", "홍")

 

Arbitrary Arguments, *args

함수에 전달할 인수의 갯수를 모르는 경우 함수 정의에서 매개변수 이름 앞에 *를 사용할 수 있습니다.
이런 식의 함수는 인수를 튜플로 처리합니다.

예제)

arguments 갯수를 모를경우에, parameter 이름 앞에 *(별표)를 추가합니다.

def my_function(*king):
    print(type(king))
    arg_len = len(king)
    for i in range(0, arg_len):
    	print(str(i) + " king is " + king[i])	

my_function("세종대왕", "징기스칸", "나폴레옹")

-- 실행결과
<class 'tuple'>
0 king is 세종대왕
1 king is 징기스칸
2 king is 나폴레옹
 

Keyword Arguments

key = value 구문을 사용하여 인수를 보낼 수도 있습니다.인수의 순서는 중요하지 않습니다.

예제) 

def my_function(pram3, pram2, pram1):
  print("The first argement is " + pram1)

my_function(pram1 = "Emil", pram2 = "Tobias", pram3 = "Linus")

-- 실행결과
The first argement is Emil
 
 

Arbitrary Keyword Arguments, **kwargs

함수에 전달할 키워드 인수의 수를 모르는 경우 함수 정의에서 매개변수 이름 앞에 ** 두 개의 별표를 추가합니다.
이런 함수는 인수를 dictionary 형식으로 인식합니다.

예제)

def my_function(**kid):
  print("His first name is " + kid["fname"])
  print("His last name is " + kid["lname"])
  
my_function(fname = "길동", lname = "홍")

-- 실행결과
His first name is 길동
His last name is 홍
 
 

Default Parameter Value

다음 예는 기본 매개변수 값을 사용하는 방법입니다.
인수 없이 함수를 호출하면 기본값을 사용합니다.

예제)

def my_function(country = "Seoul"):
  print("I am from " + country)

my_function("Sweden")
my_function()
my_function("Dubai")

-- 실행결과
I am from Sweden
I am from Seoul
I am from Dubai
 
 

Passing a List as an Argument

인수의 모든 데이터 유형(문자열, 숫자, list, dictionary 등)을 함수에 보낼 수 있으며 함수 내에서 동일한 데이터 유형으로 처리됩니다.
예를 들어 List를 인수로 보내면 함수에서도  List가 됩니다.

예제)

def my_function(food):
  for x in food:
    print(x)

fruits = ["apple", "banana", "cherry"]

my_function(fruits)

-- 실행결과
apple
banana
cherry
 

Return Values

함수가 값을 반환하도록 하려면 return 문을 사용합니다.

Example

def my_function(x):
  return 5 * x

print(my_function(3))
print(my_function(5))

-- 실행결과
15
25
 

The pass Statement

function definitions cannot be empty, but if you for some reason have a function definition with no content, put in the pass statement to avoid getting an error.

함수 정의는 반드시 해야 하지만 어떤 이유로 내용이 없는 함수 정의가 필요한 경우, 경우 오류가 발생하지 않도록 pass 문을 입력합니다. 함수 호출 시 아무것도 pass만 수행됩니다.

Example

def myfunction():
  pass

myfunction()

-- 실행결과
 

Recursion

Python also accepts function recursion, which means a defined function can call itself.

Recursion is a common mathematical and programming concept. It means that a function calls itself. This has the benefit of meaning that you can loop through data to reach a result.

The developer should be very careful with recursion as it can be quite easy to slip into writing a function which never terminates, or one that uses excess amounts of memory or processor power. However, when written correctly recursion can be a very efficient and mathematically-elegant approach to programming.

In this example, tri_recursion() is a function that we have defined to call itself ("recurse"). We use the k variable as the data, which decrements (-1) every time we recurse. The recursion ends when the condition is not greater than 0 (i.e. when it is 0).

To a new developer it can take some time to work out how exactly this works, best way to find out is by testing and modifying it.

Python은 함수 재귀 호출을 허용합니다. 즉, 함수가 자기 자신을 호출할 수 있습니다.

재귀는 일반적인 수학 및 프로그래밍 개념입니다. 이는 결과에 도달하기 위해 데이터를 반복할 수 있습니다.

무한반복 함수나 과도한 양의 메모리 또는 프로세서를 사용하는 함수를 작성할 수 있기 때문에 재귀함수 생성 시 주의해야 합니다. 올바르게 작성되면 재귀 함수는 프로그래밍에 대한 매우 효율적이고 유용한 방식이 될 수 있습니다.

이 예에서 tri_recursion()은 자체 호출("재귀")하도록 정의한 함수입니다. 우리는 데이터로 k 변수를 사용하는데, 이것은 재귀할 때마다 (-1) 감소합니다. 조건이 0보다 크지 않으면(즉, 0인 경우) 재귀가 종료됩니다.

예제

함수 재귀호출 예제

def tri_recursion(k):
  if(k > 0):
    result = k + tri_recursion(k - 1)
    print(result)
  else:
    result = 0
  return result

tri_recursion(5)

-- 실행결과
1
3
6
10
15
 
 
 
 

Lambda 함수 : link 참조

 

decorator

라이브러리

 

클래스와 객체

 

파일 다루기

 

주피터 노트북

 

pandas

 

계산기 만들기 예제

GUI 게시판

 

 

 

리터럴 상수
리터럴 상수의 예는 5 , 1.23 과 같은 숫자 또는 'This is a string' 또는 "It's a string!"과 같은 문자열입니다. .
리터럴이기 때문에 리터럴이라고 합니다. 값을 문자 그대로 사용합니다. 숫자 2는 항상 자신을 나타내며 다른 것은 표시하지 않습니다.
값을 변경할 수 없기 때문에 상수입니다. 따라서 이 모든 것을 리터럴 상수라고 합니다.

 

숫자
숫자는 주로 정수와 부동 소수점의 두 가지 유형이 있습니다.
정수의 예는 정수인 2입니다.
부동 소수점 숫자(또는 줄여서 float)의 예는 3.23 및 52.3E-4 입니다. E 표기법은 10의 거듭제곱을 나타냅니다. 이 경우,
52.3E-4 는 52.3 * 10^-4^ 를 의미합니다.
숙련된 프로그래머를 위한 참고 사항
별도의 long형은 없습니다. int 유형은 모든 크기의 정수가 될 수 있습니다.

 

문자열

문자열은 일련의 문자입니다. 문자열은 기본적으로 단어의 묶음입니다.
작성하는 거의 모든 Python 프로그램에서 문자열을 사용할 것이므로 다음 부분에 주의하십시오.
작은따옴표
'Quote me on this' 와 같은 작은따옴표를 사용하여 문자열을 지정할 수 있습니다.
따옴표 안에 있는 모든 공백(예: 공백 및 탭)은 있는 그대로 유지됩니다.
큰따옴표
큰따옴표로 묶인 문자열은 작은따옴표로 묶인 문자열과 정확히 같은 방식으로 작동합니다. 예는 "이름이 무엇입니까?"입니다. .
트리플 쿼트
삼중따옴표( """ 또는 ''' )를 사용하여 여러 줄 문자열을 지정할 수 있습니다.
삼중 따옴표. 예는 다음과 같습니다.
'''여러 줄 문자열입니다. 이것이 첫 번째 줄입니다.
이것은 두 번째 줄입니다.
"이름이 뭐예요?" 내가 물었다.
그는 "본드, 제임스 본드"라고 말했다.
'''
문자열은 변경할 수 없습니다.
즉, 문자열을 만든 후에는 변경할 수 없습니다. 이것이 나쁜 일처럼 보일 수 있지만 실제로는 그렇지 않습니다. 우리는
이것이 나중에 보게 될 다양한 프로그램의 제한 사항이 아닌 이유를 확인하십시오.
C/C++ 프로그래머를 위한 참고 사항
Python에는 별도의 char 데이터 유형이 없습니다. 실제로 필요하지 않으며 놓치지 않을 것입니다.
Perl/PHP 프로그래머를 위한 참고 사항
작은 따옴표로 묶인 문자열과 큰 따옴표로 묶인 문자열은 동일하며 어떤 방식으로도 다르지 않습니다.
포맷 방법
때때로 우리는 다른 정보에서 문자열을 구성하고 싶을 수 있습니다. 여기서 format() 메서드가 유용합니다.
다음 줄을 str_format.py 파일로 저장합니다.

 

 

 

VSCode(Visual Studio Code)로 작성한 프로그램에 argument를 전달하려면 

  • terminal에서 수행 시 argument를 전달하거나
  • VSCode에서 수행 시 launch.json 파일에 정의하여 전달하면 된다.

본 문서에서는 argument를 launch.json 파일에 설정하는 방법을 설명한다.

launch.json(디버깅 환경설정 파일) 설정

 launch.json(디버깅 환경설정 파일) 파일이 없다면 VSCode의 좌측 메뉴에서 [Run and Debug] 을 클릭하고

아래와 같이 나타난 화면에서 "create a launch.json file"을 클릭합니다.

"Select a debug configuration" 에서 Python File 을 선택합니다.

아래와 같이 launch.json 파일이 생성되는데 

"args": ["60"], 을 각 개인의 argument에 맞게 추가해 줍니다. 본문에서는 ["60"] 이렇게 설정했는데 "60" 은 실행되는 python 파일에 전달하는 첫번째 argument 값입니다.

argument 가 여러 개일 경우 ["arg1", "arg2", "arg3"] 식으로 나열하면 됩니다. 단, 숫자는 안되고 문자형식만 됩니다. 즉, argument가 60인 경우, "60" 으로 입력해야 합니다. 

## launch.json

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python: Current File",
            "type": "python",
            "request": "launch",
            "program": "${file}",
            "args": ["60"],
            "console": "integratedTerminal"
        }
    ]
}

이제 VSCode에서 작성한 argument가 필요한 python 파일을 실행해 보면, 위에서 설정한 argument 값을 받아서 처리하는 것을 확인할 수 있습니다.

 

 
아래는 launch.json 파일의 속성들에 대한 설명입니다.

launch.json

다음 속성은 실행구성에 필수 요소입니다.

  • type : 실행구성에 사용할 디버거 유형(node, php, python..등)
  • request : 실행구성의 요청유형 현재는 'launch', 'attach' 입니다.
  • name : 디버그 드롭다운에 나타나는 디버그 런치 이름

다음은 실행구성에서 사용할수 있는 선택적 속성입니다.

  • preLaunchTask : 디버그 세션이 시작되기 전에 작업을 시작하려면 이 속성을 tasks.json 에 지정된 이름으로 설정해야합니다.
  • internalConsoleOptions : 디버깅 세션동안 다버그 콘솔 패널의 가시성을 제어합니다.
  • debugServer : 디버그를 시작하는 대신 지정 포트에 연결합니다.

많은 디버거가 다음 속성 중 일부를 지원합니다.

  • program : 디버거를 시작할 때 실행할 실행 파일 또는 파일
  • args: 디버깅 할 프로그램에 전달된 인수
  • env: 환경 변수
  • cwd: 의존성 및 기타 파일을 찾기위한 현재 작업 디렉토리
  • port: 실행중인 프로세스에 연결할 때의 포트
  • stopOnEntry: 프로그램이 시작되면 즉시 중단
  • console: 어떤 종류를 콘솔을 사용할지 지정. 예를 들어 internalConsole, integratedTerminal, externalTerminal.

vscode는 문자열 내부의 변수 대체를 지원 launch.json하며 다음과 같이 미리 정의 된 변수가 있습니다.

  • ${workspaceFolder} : vscode에서 연 폴더의 경로
  • ${workspaceFolderBasename} : vscode에서 슬래시없이 열리는 폴더 이름 (/)
  • ${file} : 현재 열려있는 파일
  • ${relativeFile} : workspaceFolder 에서 현재 열린 파일
  • ${fileBasename} : 현재 열려있는 파일의 기본 이름
  • ${fileBasenameNoExtension} : 파일 확장명이 없는 현재 열린 파일의 기본 이름
  • ${fileDirname} : 현재 열려있는 파일의 디렉토리 이름
  • ${fileExtname} : 현재 열려있는 파일의 확장자
  • ${cwd} : 시작시 태스크 러너의 현재 작업 디렉토리
  • ${lineNumber} : 활성 파일에서 현재 선택된 행 번호

import만 해도 sqlite3 DB를 사용할 수 있다고?

 

sqlite3 모듈은 파이썬 표준 라이브러리(파이썬이 설치될 때 기본적으로 설치되는 모듈)로 SQLite에 대한 인터페이스를 제공합니다. 표준 라이브러리인 sqlite3 모듈을 이용하면 SQLite 데이터베이스를 이용할 수 있습니다.

Python에서 SQLite 을 사용하기 위해서는 보통 pysqlite 으로 불리우는 Language binding (주: SQLite 라이브러리를 내부에서 사용)을 사용하는데, pysqlite 은 Python 2.5 이상에서 기본적으로 내장되어 있다.

pysqlite 를 import 하기 위해서는 "import sqlite3" 와 같이 import만 하면 된다.

 

파이썬 SQLite 라이브러리 불러오기 및 버전 확인

우선 라이브러리를 불러와서 “sqlite3 라이브러리”의 버전과 “SQLite(DB 엔진)” 버전을 각각 출력해보자.

import sqlite3
 
print(sqlite3.version)
print(sqlite3.sqlite_version)

 

DB 연결, 커서 획득

이제 진짜 DB를 생성하는 쿼리를 실행해보자.

# DB 생성 (오토 커밋)
conn = sqlite3.connect("test.db", isolation_level=None)
 
# 커서 획득
c = conn.cursor()
 
# 테이블 생성 (데이터 타입은 TEST, NUMERIC, INTEGER, REAL, BLOB 등)
c.execute("CREATE TABLE IF NOT EXISTS table1 \
(id integer PRIMARY KEY, name text, birthday text)")

일단 "test.db"라는 데이터베이스를 생성해봤다. 브라우저로 열어서 보면 잘 보일 거다. 엑셀에서 첫 행을 적어두었다 생각하면 된다.

c.execute("CREATE TABLE IF NOT EXISTS 테이블이름()")안에 문자열로 필드(열) 이름과 데이터 타입을 작성해주면 된다.

 

CREATE TABLE IF NOT EXITSTS 테이블_이름() 이라는 쿼리는 말 그대로 “테이블_이름이라는 테이블이 없으면 테이블을 생성해라”라는 의미다. 있으면 있는 걸 그대로 쓰는 거고. 그 안에 필드를 나열해주면 되는데 필드명 > 테이터 타입 순으로 입력한다. PRIMARY KEY는 테이블 내에 있는 레코드를 식별하는 고유 키를 말하며, 유일한 것이어야 한다.

개념 1. commit & rollback

위에서 sqlite3.connect() 괄호 안에 isolation_level=None이라고 명시했는데 이는 (실습을 위해) 쿼리문을 실행하여 DB에 즉시 반영, 즉시 자동 커밋을 하기 위함이다.

commit(커밋)은 “변경사항을 DB에 반영한다”는 뜻이라 commit을 하지 않으면 수정(추가/갱신/삭제 등) 작업에 대한 기록을 컴퓨터 메모리 같은 데 임시로 가지고 있을 뿐 실제로 DB에는 반영하지 않는다. 최종적으로 DB를 수정을 하려면 마지막에 반드시 conn.commit()이라는 명령을 실행해주어야 한다.

참고로 이것과 commit과 반대되는 개념으로 rollback(롤백)이 있다. 이전 이력으로 되돌린다는 뜻. conn.rollback()으로 명령한다.

개념 2. cursor

파이썬에서 파일을 읽고 쓰려면 커서를 가져와야 한다. 그래서 conn.cursor()로 일단 커서를 생성한다.

데이터 삽입하기

데이터 한 건을 넣으려면 c.execute(“INSERT INTO table1 VALUES()”)라고 해서 값을 직접 넣자.

# 데이터 삽입 방법 1
c.execute("INSERT INTO table1 \
VALUES(1, 'LEE', '1987-00-00')")

테이블 안에 데이터가 들어간 걸 확인할 수 있다. 그런데 이렇게 하는 건 필드명과 순서를 정확히 알고 있다는 전제 하에 가능한 거다.

만약 정석으로 데이터를 삽입하려면 아래와 같이 튜플로 넣어주는 게 좋다.

# 데이터 삽입 방법 2
c.execute("INSERT INTO table1(id, name, birthday) \
VALUES(?,?,?)", \
(2, 'KIM', '1990-00-00'))

이번엔 튜플이나 리스트 형태의 데이터 세트를 한 번에 삽입하는 방법도 알아야겠지. c.executemany()를 사용하면 된다.

test_tuple = (
(3, 'PARK', '1991-00-00'),
(4, 'CHOI', '1999-00-00'),
(5, 'JUNG', '1989-00-00')
)
 
c.executemany("INSERT INTO table1(id, name, birthday) VALUES(?,?,?)", test_tuple)

끝이다. 리스트로도 가능하다.

데이터 불러오기

이제 데이터들이 잘 있나 확인해야겠지. 우선 데이터를 모두 선택한 다음에 c.fetchone()으로 하나씩 출력해보자.

c.execute("SELECT * FROM table1")
print(c.fetchone())
print(c.fetchone())
print(c.fetchall())

c.fetchone()을 사용하면 한 줄씩 출력하는 걸 알 수 있다. 커서가 이동하기 때문이다. 그리고 이후에 c.fetchall()이라는 걸 사용해서 전체를 가져와서 출력하더라도 이미 읽은 지점 이후에 있는 것들만 출력된다. (참고로 fetch는 “가져오다”라는 뜻.)

그래서 만약 전체 데이터를 출력하고 싶다면 이렇게 전체를 다시 읽어 놓고 해야 한다.

c.execute("SELECT * FROM table1")
print(c.fetchall())

그리고 이렇게 가져온 데이터는 리스트 형태로 출력되는 걸 알 수 있는데, 결국 반복문을 돌 수 있다는 뜻이다.

# 방법 1
c.execute("SELECT * FROM table1")
for row in c.fetchall():
print(row)
 
# 방법 2
for row in c.execute("SELECT * FROM table1 ORDER BY id ASC"):
print(row)

방법 2가 한 줄 덜 쓰긴 하지만 가독성이 떨어져서 차라리 방법 1이 나은 것 같다.

데이터 조회하기 (필터링)

원하는 데이터만 찾아서 가져올 수도 있어야겠지. SQL에서 WHERE 문을 써야 한다.

그런데 이때 방법이 이것저것 있다. 포매팅을 할 수도 있고, 딕셔너리 형식으로 설정해서 가져올 수도 있다. 좀 복잡하니 해보면서 익숙한 방법을 사용하면 된다.

여기서는 id라는 변수가 어떤 녀석인 것들만 선택해서 가져올지 짜봤다.

# 방법 1
param1 = (1,)
c.execute('SELECT * FROM table1 WHERE id=?', param1)
print('param1', c.fetchone())
print('param1', c.fetchall())
 
# 방법 2
param2 = 1
c.execute("SELECT * FROM table1 WHERE id='%s'" % param2) # %s %d %f
print('param2', c.fetchone())
print('param2', c.fetchall())
 
# 방법 3
c.execute("SELECT * FROM table1 WHERE id=:Id", {"Id": 1})
print('param3', c.fetchone())
print('param3', c.fetchall())
 
# 방법 4
param4 = (1, 4)
c.execute('SELECT * FROM table1 WHERE id IN(?,?)', param4)
print('param4', c.fetchall())
 
# 방법 5
c.execute("SELECT * FROM table1 WHERE id In('%d','%d')" % (1, 4))
print('param5', c.fetchall())
 
# 방법 6
c.execute("SELECT * FROM table1 WHERE id=:id1 OR id=:id2", {"id1": 1, "id2": 4})
print('param6', c.fetchall())

처음엔 좀 어렵지만, 익숙해지면 어렵지 않게 쓸 수 있겠지.

데이터 수정하기

위에서 조회할 때 사용한 WHERE 문과 함께 UPDATE를 통해 갱신하는 거다.

id가 무엇인 녀석을 찾아서 name을 새로운 걸로 고쳐보자. 이렇게.

# 방법 1
c.execute("UPDATE table1 SET name=? WHERE id=?", ('NEW1', 1))
 
# 방법 2
c.execute("UPDATE table1 SET name=:name WHERE id=:id", {"name": 'NEW2', 'id': 3})
 
# 방법 3
c.execute("UPDATE table1 SET name='%s' WHERE id='%s'" % ('NEW3', 5))
 
# 확인
for row in c.execute('SELECT * FROM table1'):
print(row)

편한 방법으로 사용하자.

데이터 삭제하기

테이블에 있는 특정 데이터를 지우려면 WHERE과 DELETE를 조합하면 된다.

# 방법 1
c.execute("DELETE FROM table1 WHERE id=?", (1,))
 
# 방법 2
c.execute("DELETE FROM table1 WHERE id=:id", {'id': 3})
 
# 방법 3
c.execute("DELETE FROM table1 WHERE id='%s'" % 5)
 
# 확인
for row in c.execute('SELECT * FROM table1'):
print(row)

만약 테이블에 있는 데이터 전체를 지우려면 conn.execute() 안에 쿼리문을 써주면 된다.

# 방법 1
# conn.execute("DELETE FROM table1")
 
# 방법 2
print(conn.execute("DELETE FROM table1").rowcount)

뒤에 rowcount를 붙여주면 지운 행 개수를 돌려준다.

DB 연결 해제

데이터베이스를 연결해서 이런저런 수정을 했으면 마지막엔 그 연결을 해제해야 한다. 그래서 항상 conn.close() 명령으로 마무리한다.

DB 백업하기 (dump)

데이터베이스는 항상 dump를 통해 백업을 해놓는 게 중요하다. 그래야 나중에 다른 PC에서도 이걸 그대로 재구성할 수 있다.

with conn:
with open('dump.sql', 'w') as f:
for line in conn.iterdump():
f.write('%s\n' % line)
print('Completed.')

이래놓고 sql 파일을 열어보면 신기하게 아래와 같이 생겼다.

BEGIN TRANSACTION;
CREATE TABLE table1(id integer PRIMARY KEY, name text, birthday text);
INSERT INTO "table1" VALUES(1,'LEE','1987-00-00');
INSERT INTO "table1" VALUES(2,'KIM','1990-00-00');
INSERT INTO "table1" VALUES(3,'PARK','1991-00-00');
INSERT INTO "table1" VALUES(4,'CHOI','1999-00-00');
INSERT INTO "table1" VALUES(5,'JUNG','1989-00-00');
COMMIT;

실제로 DB를 아예 재구성하는 SQL 쿼리들이 작성되어 있다.

 

원문: https://hleecaster.com/python-sqlite3/

파이썬 소스코드를 보다보면 아래와 같이 if __name__ == "__main__": 구문을 많이 볼 수 있습니다.

if __name__ == "__main__":
    # Execute when the module is not initialized from an import statement.
    main()

위 문장이 어떤 의미인지 알아볼까요.

 

 __name__이란?

모듈의 이름을 담고 있는 파이썬 내장 변수입니다.

python.py 라는 파일이 있다면 __name__는 .py 확장자를 제외한 python이 됩니다.

 

__main__이란?

__main__은 최상위 코드가 실행되는 환경의 이름입니다. "최상위 코드"는 프로그램 실행 시 첫 번째로 실행되는 Python 모듈입니다. 프로그램 구동에 필요한 다른 모듈들을 가져오기(import) 때문에 "최상위"라고 합니다.

"최상위 코드"를 애플리케이션의 진입점(entry point)이라고도 합니다.

모듈은 __name__을 확인하여 최상위 환경에서 실행 중인지의 여부를 확인할 수 있습니다

모듈이 최상위 코드 환경에서 실행되면 해당 __name__은 '__main__' 문자열로 설정됩니다.

Python 모듈 또는 패키지를 imort하면 __name__은 모듈 이름으로 설정됩니다. 일반적으로 .py 확장자가 없는 파일 이름입니다.

 

<예제>

모듈이 메인 프로그램인 경우

first_module.py 파일을 만들고 아래와 같이 입력합니다.

##  first_module.py
print('__name__ value :', __name__)

아래와 같이 python first_module.py를 명령창에서 수행하면 __name__ 값이 '__main__'인 것을 알 수 있습니다. 이는 위에서 설명한 최상위 코드(main program)에서 실행했기 때문입니다.

$ python first_module.py
__name__ value : __main__

즉, 파이썬 인터프리터는 문자열 '__main__'을 __name__변수에 할당합니다.

# It's as if the interpreter inserts this at the top
# of your module when run as the main program.
__name__ = "__main__" 

 

모듈을 import하는 경우

다른 모듈이 위에서 작성한 모듈을 import 할 경우입니다. second_module.py 파일을 아래와 같이 생성합니다.

# second_module.py
import first_module

print('__name__ value :', __name__)

아래와 같이 python second_module.py를 명령창에서 수행하면 import한 first_module의 __name__ 값이 'first_module'인 것을 알 수 있습니다. 이는 위에서 설명한대로 .py 확장자를 제외한 파일명으로 설정됩니다. 

$ python second_module.py
__name__ value : first_module
__name__ value : __main__

파이썬 인터프리터는 first_module.py 파일을 검색하고 해당 모듈을 실행하기 전에 import  name문에서 __name__변수로 모듈 이름(확장자 .py를 제외한 이름)을 지정합니다.

# It's as if the interpreter inserts this at the top
# of your module when it's imported from another module.
__name__ = "first_module"

 

위의 예제에서처럼 import를 하게되면 import된 모듈이 실행이 됩니다. 위와같이 의도치 않게 import module이 실행되는 것을 막기 위해 

if __name__ == "__main__": 구문을 사용합니다.

즉, 메인 프로그램에서 실행했을 경우에만 수행을 하도록 제어하는 것입니다.

 

위에서 사용한 first_module.py과 second_module.py 파일을 아래와 같이 수정합니다.

##  first_module.py
if __name__ == "__main__": 
	print('__name__ value :', __name__)
# second_module.py
import first_module

if __name__ == "__main__": 
	print('__name__ value :', __name__)

아래와 같이 다시 second_module.py 를 실행합니다. first_module은 최상위 코드가 아니므로 if __name__ == "__main__":  구문에 의해 print 문장이 수행되지 않았습니다.

$ python second_module.py
__name__ value : __main__

이와같은 사유로 if __name__ == "__main__": 

구문을 습관적으로 사용해야 합니다.

 

가장 기본적인 사항만 설명하였으니, 상세 내용은 아래를 참조하세요. __name__과 __main__ 관련하여 위에서 설명한 것 이외에도 많은 내용들이 있습니다.

 

아래 내용은 파이썬 document의 내용입니다.

내용이 많고 복잡합니다.

https://docs.python.org/3/library/main.html?highlight=main

 

 

__main__ — Top-level code environment — Python 3.10.1 documentation

Both of these mechanisms are related to Python modules; how users interact with them and how they interact with each other. They are explained in detail below. If you’re new to Python modules, see the tutorial section Modules for an introduction. __name_

docs.python.org

Python에서 __main__이라는 이름은 다음 두 가지 구성에 사용됩니다.

  • 프로그램의 최상위 코드 환경 이름으로,  __name__ == '__main__' 표현식
  • Python 패키지의 __main__.py 파일

Python 모듈 또는 패키지를 가져올 때 __name__은 모듈의 이름으로 설정됩니다.

모듈이 최상위 코드 환경에서 실행되면 해당 __name__이 '__main__' 문자열로 설정됩니다.

 

Python 모듈 또는 패키지를 가져오면 __name__이 모듈 이름으로 설정됩니다. 일반적으로 .py 확장자가 없는 Python 파일 자체의 이름입니다.

 

"최상위 코드 환경"이란?
__main__은 최상위 코드가 실행되는 환경의 이름입니다. "최상위 코드"는 실행을 시작하는 첫 번째 Python 모듈입니다. 프로그램에 필요한 다른 모든 모듈을 가져오기 때문에 "최상위"라고 합니다. "최상위 코드"를 애플리케이션의 진입점이라고도 합니다.

 

결과적으로 모듈은 __name__을 확인하여 최상위 환경에서 실행 중인지 여부를 확인할 수 있습니다. 이는 모듈이 import 문에서 초기화되지 않을 때 조건부로 코드를 실행하는 일반적인 관용구를 허용합니다.

if __name__ == '__main__':
    # Execute when the module is not initialized from an import statement.
    ...

관용적 사용법
일부 모듈에는 명령줄 인수를 구문 분석하거나 표준 입력에서 데이터를 가져오는 것과 같이 스크립트 전용 코드가 포함되어 있습니다. 예를 들어 단위 테스트를 위해 이와 같은 모듈을 다른 모듈에서 가져온 경우 스크립트 코드도 의도하지 않게 실행됩니다.

여기서 if __name__ == '__main__' 코드 블록을 사용하면 편리합니다. 이 블록 내의 코드는 모듈이 최상위 환경에서 실행되지 않는 한 실행되지 않습니다.

if __name__ == '__main__' 아래 블록에 가능한 한 적은 수의 명령문을 넣으면 코드 명확성과 정확성이 향상될 수 있습니다. 대부분의 경우 main이라는 함수는 프로그램의 기본 동작을 캡슐화합니다. 

# echo.py

import shlex
import sys

def echo(phrase: str) -> None:
   """A dummy wrapper around print."""
   # for demonstration purposes, you can imagine that there is some
   # valuable and reusable logic inside this function
   print(phrase)

def main() -> int:
    """Echo the input arguments to standard output"""
    phrase = shlex.join(sys.argv)
    echo(phrase)
    return 0

if __name__ == '__main__':
    sys.exit(main())  # next section explains the use of sys.exit

 

모듈이 main 함수 내부에 코드를 캡슐화하지 않고 대신 if __name__ == '__main__' 블록 내에 직접 넣는 경우, phrase 변수는 전체 모듈에 대해 global 변수가 됩니다. 모듈 내의 다른 함수가 의도하지 않게 local 변수 대신 global 변수를 사용할 수 있으므로 오류가 발생하기 쉽습니다. main 함수는 이 문제를 해결합니다.

main 함수를 사용하면 echo 함수 자체가 격리되고 다른 어느 곳에서나 import할 수 있다는 이점이 있습니다.

echo.py를 가져오면 echo와 main 함수가 정의되지만 둘 다 호출되지 않습니다.

왜냐하면 __name__ != '__main__' 때문입니다.

 

패키징 고려 사항
주요 기능은 콘솔 스크립트의 진입점으로 지정하여 명령줄 도구를 만드는 데 자주 사용됩니다. 이 작업이 완료되면 pip는 main의 반환 값이 sys.exit()에 전달되는 템플릿 스크립트에 함수 호출을 삽입합니다. 예를 들어:

sys.exit(main())
main에 대한 호출은 sys.exit()에 래핑되어 있으므로 함수가 sys.exit()에 대한 입력으로 허용되는 일부 값을 반환할 것입니다. 일반적으로 정수 또는 None(함수에 return 문이 없는 경우 암시적으로 반환됨)입니다.

이 규칙을 따랐다면 모듈은 나중에 pip-installable 패키지의 콘솔 스크립트 진입점으로 패키징할 때와 마찬가지로 직접 실행할 때(예: python3 echo.py) 동일한 동작을 갖게 됩니다.

특히 main 함수에서 문자열을 반환할 때 주의하십시오. sys.exit()는 문자열 인수를 실패 메시지로 해석하므로 프로그램은 실패를 나타내는 종료 코드 1을 가지며 문자열은 sys.stderr에 기록됩니다. 

 

__main__.py in Python Packages

일반적으로 __main__.py 파일은 패키지에 대한 명령줄 인터페이스를 제공하는 데 사용됩니다.

다음 패키지 "bandclass"를 가정해봅니다.

bandclass
  ├── __init__.py
  ├── __main__.py
  └── student.py

__main__.py는 패키지 자체가 -m 플래그를 사용하여 명령줄에서 직접 호출될 때 실행됩니다. 예를 들어:

$ python3 -m bandclass

이 명령은 __main__.py가 실행되도록 합니다. 이 메커니즘을 사용하는 방법은 작성 중인 패키지의 특성에 따라 다르지만 이 경우 교사가 학생을 검색하도록 허용하는 것이 합리적일 수 있습니다.

# bandclass/__main__.py

import sys
from .student import search_students

student_name = sys.argv[2] if len(sys.argv) >= 2 else ''
print(f'Found student: {search_students(student_name)}')

from .student import search_students는 상대적 import의 예입니다. 이 import 스타일은 패키지 내에서 모듈을 참조할 때 사용할 수 있습니다.

 

관용적 용법

__main__.py의 내용은 일반적으로 if __name__ == '__main__' 블록으로 구분되지 않습니다. 대신 해당 파일은 다른 모듈에서 실행하기 위해 짧게 유지됩니다. 그런 다음 이러한 다른 모듈은 단위 테스트를 쉽게 수행할 수 있으며 적절하게 재사용할 수 있습니다.

if __name__ == '__main__' 블록을 사용하면 패키지 내의 __main__.py 파일에 대해 예상대로 작동합니다. import가 되면 __name__ 속성에 패키지의 경로가 포함되기 때문입니다.

>>>
>>> import asyncio.__main__
>>> asyncio.__main__.__name__
'asyncio.__main__'

이것은 .zip 파일의 루트 디렉토리에 있는 __main__.py 파일에는 작동하지 않습니다. 따라서 일관성을 위해 위에서 언급한 venv와 같은  __main__.py가 더 좋습니다.

 

import __main__

Python 프로그램이 시작된 모듈에 관계없이 동일한 프로그램 내에서 실행되는 다른 모듈은 __main__ 모듈을 가져와서 최상위 환경의 범위(네임스페이스)를 가져올 수 있습니다. 이것은 __main__.py 파일을 가져오는 것이 아니라 특별한 이름 '__main__'을 받은 모듈을 가져옵니다.

다음은 __main__ 네임스페이스를 사용하는 예제 모듈입니다.

 

Here is an example module that consumes the __main__ namespace:

# namely.py

import __main__

def did_user_define_their_name():
    return 'my_name' in dir(__main__)

def print_user_name():
    if not did_user_define_their_name():
        raise ValueError('Define the variable `my_name`!')

    if '__file__' in dir(__main__):
        print(__main__.my_name, "found in file", __main__.__file__)
    else:
        print(__main__.my_name)

이 모듈의 사용 예는 다음과 같습니다

# start.py

import sys

from namely import print_user_name

# my_name = "Dinsdale"

def main():
    try:
        print_user_name()
    except ValueError as ve:
        return str(ve)

if __name__ == "__main__":
    sys.exit(main())

 

프로그램을 실행하면 결과는 다음과 같습니다.

$ python3 start.py
Define the variable `my_name`!

 

프로그램의 종료 코드는 오류를 나타내는 1이 됩니다. my_name = "Dinsdale"이 있는 줄의 주석 처리를 제거하면 프로그램이 수정되고 이제 성공을 나타내는 상태 코드 0으로 종료됩니다

$ python3 start.py
Dinsdale found in file /path/to/start.py

__main__을 가져오면 시작 모듈의 if __name__ == "__main__" 블록에 있는 스크립트 사용을 위한 최상위 코드를 의도하지 않게 실행하는 데 문제가 발생하지 않습니다. 왜 이런 식으로 작동할까요?

Python은 인터프리터 시작 시 sys.modules에 빈 __main__ 모듈을 삽입하고 최상위 코드를 실행합니다.

이 예에서는 한 줄씩 실행하고 namely를 import하는 시작 모듈입니다. 차례로 __main__(실제로 시작됨)을 import합니다.

이것이 import cycle입니다.

부분적으로 채워진 __main__ 모듈이 sys.modules에 있기 때문에 Python은 이를 namely로 전달합니다. 

Python REPL은 "최상위 환경"의 또 다른 예이므로 REPL에 정의된 모든 것이 __main__ 범위의 일부가 됩니다.

>>>
>>> import namely
>>> namely.did_user_define_their_name()
False
>>> namely.print_user_name()
Traceback (most recent call last):
...
ValueError: Define the variable `my_name`!
>>> my_name = 'Jabberwocky'
>>> namely.did_user_define_their_name()
True
>>> namely.print_user_name()
Jabberwocky

이 경우 __main__ 범위는 대화형이므로 __file__ 속성을 포함하지 않습니다.
__main__ 범위는 pdb 및 rlcompleter의 구현에 사용됩니다.

 

파이썬은 왜 프로그램의 시작점이 정해져 있지 않나요?

파이썬이 처음에 개발 될 당시에는 리눅스/유닉스에서 사용하는 스크립트 언어 기반이었기 때문에 프로그램의 시작점이 따로 정해져 있지 않았습니다. 보통 리눅스/유닉스의 스크립트 파일은 파일 한 개로 이루어진 경우가 많은데, 이 스크립트 파일 자체가 하나의 프로그램이다 보니 시작점이 따로 필요하지 않습니다. 하지만 C 언어나 자바같은 언어는 처음 만들어질 때부터 소스 파일을 여러 개 사용했기 때문에 여러 소스 파일의 함수들 중에서도 시작 함수(main)를 따로 정해 놓았습니다.

+ Recent posts