Skip to content

Python

Python Module Index

파이썬(Python)은 1991년 프로그래머인 귀도 반 로섬(Guido van Rossum) 이 발표한 고급 프로그래밍 언어로, 플랫폼 독립적이며 인터프리터식, 객체지향적, 동적 타이핑(dynamically typed) 대화형 언어이다. 파이썬이라는 이름은 귀도가 좋아하는 코미디 〈Monty Python's Flying Circus〉에서 따온 것이다.

파이썬은 비영리의 파이썬 소프트웨어 재단이 관리하는 개방형, 공동체 기반 개발 모델을 가지고 있다. C파이썬 구현이 사실상의 표준이다.

Category

Native

  • CPython (구글링 시 "Embedded Python"로 검색하면 된다)
  • HPy - 파이썬을 위한 더 나은 C API
  • Boost:Python
  • pybind11
  • Manylinux - Python 커뮤니티에서 개발된 리눅스 배포판들 간의 호환성을 보장하기 위한 표준화된 방법입니다.

Python Enhancement Proposals (PEP) Index

  • Python Enhancement Proposals (PEP)
  • PEP 8 - Style Guide for Python Code
  • PEP 405 - Python Virtual Environments
  • PEP 425 - Compatibility Tags for Built Distributions
  • PEP 427 (wheel) - The Wheel Binary Package Format 1.0
  • PEP 432 - Restructuring the CPython startup sequence (CPython 시작 시퀀스 재구성)
  • PEP 440 - Version Identification and Dependency Specification
  • PEP 484 - Type Hints, pyi에 해당하는 "Stub Files"에 대한 내용도 포함.
  • PEP 498 - Literal String Interpolation (f"..." 이거)
  • PEP 525 - Asynchronous Generators (__aiter__, __anext__, async for, typing.AsyncGenerator)
  • PEP 544 - Protocols: Structural subtyping (static duck typing)
  • PEP 578 - Python Runtime Audit Hooks
  • PEP 685 - Comparison of extra names for optional distribution dependencies
  • PEP 692 - Using TypedDict for more precise **kwargs typing
  • PEP 703 - Making the Global Interpreter Lock Optional in CPython (nogil)
  • PEP 3333 - Python Web Server Gateway Interface v1.0.1

How to install

Install required dependencies

RHEL/CentOS Linux:

sudo yum-builddep python python-libs
sudo yum install libffi-devel sqlite-devel zlib zlib-devel

Ubuntu/Debian Linux:

sudo apt-get build-dep python
sudo apt-get install libffi-dev libgdbm-dev libsqlite3-dev libssl-dev zlib1g-dev

갱신됨. 하단의 #Debian 계열 종속성 업데이트 항목 참조.

SUSE Linux

sudo zypper install automake fdupes gcc gcc-c++ gcc-fortran gdbm-devel gettext-tools gmp-devel intltool libbz2-devel libexpat-devel libffi-devel libnsl-devel lzma-devel make ncurses-devel netcfg openssl-devel pkgconfig readline-devel sqlite-devel xz zlib-devel

Python 2.7 on CentOS 6

CentOS 에서는 yum 이라는 리눅스 패키지 관리 툴/프로그램을 이용하게 되는데 그것이 python 2.6과 매우 밀접한 연관이 있어서 마구잡이로 2.7로 업그레이드하게 되면 yum을 사용하지 못하는 문제가 생긴다.

때문에 아래와 같이 개인 계정에 맞춰 설치해야 한다.

yum groupinstall "Development tools"
yum install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel

## Download Python 2.7 source code & extract.
cd ~/Downloads
wget --no-check-certificate https://www.python.org/ftp/python/2.7.6/Python-2.7.6.tar.xz
tar xf Python-2.7.6.tar.xz
cd Python-2.7.6
LDFLAGS=-fPIC && ./configure --enable-shared --prefix=/home/username/local
make && make altinstall

Debian 계열 종속성 업데이트

sudo apt-get update; sudo apt-get install make build-essential libssl-dev zlib1g-dev \
libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm \
libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev
sudo apt-get install build-essential gdb lcov pkg-config \
      libbz2-dev libffi-dev libgdbm-dev libgdbm-compat-dev liblzma-dev \
      libncurses5-dev libreadline6-dev libsqlite3-dev libssl-dev \
      lzma lzma-dev tk-dev uuid-dev zlib1g-dev

비표준 SSL 경로 추가

빌드할 때 ./configure 단계에서 --with-openssl={openssl-install-root-dir}옵션을 추가하면 된다.

Tip

  • 중요한 점은 make install이 아닌, make altinstall를 사용해야 한다. 1
  • 참고로 configureprefix옵션은 절대경로로 입력해야 한다. 그렇지 않으면 다음과 같은 에러를 확인할 수 있다.
    configure: error: expected an absolute directory name for --prefix: ~/local

기존 Python 의 컴파일 옵션 확인 방법

Python:sysconfig 에서 확인 가능. 간단히:

python3 -m sysconfig | less

Python compile

프로그램 시작(load) 시간을 줄이기 위해, 바이트 컴파일된 pyc파일을 사용 가능하다. 컴파일 방법은 아래와 같이 커맨드라인 인터프리터에서 특정 파일을 컴파일 할 수도 있고,

import py_compile
py_compile.compile('spam.py')

현재 디렉토리의 모든 *.py파일을 컴파일할 수도 있다.

$ python -m compileall .

Python files

파이선 관련 파일에 대하여 설명한다.

  • *.py는 파이썬 텍스트 소스파일이다.
  • *.pyc는 컴파일된 파이썬 바이너리 파일이다. -O 옵션을 통해 생성 가능하다.
  • *.pyo는 최적화된(Optimized) 컴파일된 파이썬 바이너리 파일이다. -OO 옵션을 통해 생성 가능하다.
  • *.pyd는 기본적으로 Windows DLL 파일이다.2

Bootstrap script

#!/usr/bin/env bash

ROOT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" || exit; pwd)

OPY_PATH=$(which opy 2> /dev/null)
PYTHON3_PATH=$(which python3 2> /dev/null)
PYTHON_PATH=$(which python 2> /dev/null)

function run_base_python
{
    if [[ -x "$OPY_PATH" ]]; then
        "$OPY_PATH" "$@"
    elif [[ -x "$PYTHON3_PATH" ]]; then
        "$PYTHON3_PATH" "$@"
    elif [[ -x "$PYTHON_PATH" ]]; then
        "$PYTHON_PATH" "$@"
    else
        echo "The python executable could not be found." 1>&2
        exit 1
    fi
}

VENV_HOME=$ROOT_DIR/.venv
VENV_PYTHON=$VENV_HOME/bin/python3

if [[ ! -d "$VENV_HOME" ]]; then
    run_base_python -m venv "$VENV_HOME"
    if [[ -x "$VENV_PYTHON" ]]; then
        "$VENV_PYTHON" -m pip install -r "$ROOT_DIR/requirements.txt"
    fi
fi
if [[ ! -x "$VENV_PYTHON" ]]; then
    echo "The venv's python executable could not be found." 1>&2
    exit 1
fi

export PYTHONPATH
PYTHONPATH=$ROOT_DIR:$PYTHONPATH

"$VENV_PYTHON" "$@"

Python Compile

Dunder

Dunder (Double Underscore). 파이썬에서 __name__ 와 같이 특수한 용도로 사용되는 심볼들을 통칭한다.

subclasshook

반드시 클래스 메서드로 정의되어야 합니다.

하위 클래스가 이 ABC의 하위 클래스로 간주되는지 확인하십시오. 이는 ABC의 하위 클래스로 간주하려는 모든 클래스에 대해 Register()를 호출할 필요 없이 issubclass()의 동작을 추가로 사용자 정의할 수 있음을 의미합니다. (이 클래스 메서드는 ABC의 subclasscheck() 메서드에서 호출됩니다.)

이 메서드는 True, False 또는 NotImplemented를 반환해야 합니다. True를 반환하면 하위 클래스는 이 ABC의 하위 클래스로 간주됩니다. False를 반환하는 경우 하위 클래스는 일반적으로 ABC의 하위 클래스인 경우에도 이 ABC의 하위 클래스로 간주되지 않습니다. NotImplemented를 반환하면 일반적인 메커니즘을 사용하여 하위 클래스 검사가 계속됩니다.

os.PathLike 예시:

class PathLike(abc.ABC):

    """Abstract base class for implementing the file system path protocol."""

    @abc.abstractmethod
    def __fspath__(self):
        """Return the file system path representation of the object."""
        raise NotImplementedError

    @classmethod
    def __subclasshook__(cls, subclass):
        if cls is PathLike:
            return _check_methods(subclass, '__fspath__')
        return NotImplemented

    __class_getitem__ = classmethod(GenericAlias)

cause

raise ... from e 처럼 from 으로 지정된 예외를 획득할 수 있다.

try:
    try:
        raise ValueError("원래 예외")
    except ValueError as e:
        raise RuntimeError("새로운 예외") from e
except RuntimeError as new_exception:
    original_exception = new_exception.__cause__
    print(f"새 예외: {new_exception}")
    print(f"원래 예외: {original_exception}")

class_getitem

Python:typing#Generics 항목 참조.

init_subclass

Python:typing#Generics 항목 참조.

Structural Pattern Matching (match ~ case 문)

파이썬 3.10 부터 도입된 문법이다. match ~ case 문.

match subject:
    case <pattern_1>:
        <action_1>
    case <pattern_2>:
        <action_2>
    case <pattern_3>:
        <action_3>
    case _:
        <action_wildcard>
  • Unpacking
  • Starred unpacking
  • Partial assignment
  • Class & Class attribute matching
  • Complex pattern with wildcard
  • Guard

Getter and Setter

Python:@property#Getter and Setter 항목 참조.

Make Python stack overflow and core dump

import sys

def recursive_sum(x):
    return x + recursive_sum(x - 1) if x > 0 else 0

print(f'Default sys.getrecursionlimit()={sys.getrecursionlimit()}')
print(f'recursive_sum(100)={recursive_sum(100)}')
print(f'recursive_sum(1000)={recursive_sum(1000)}')

String interpolation

자세한 내용은 String interpolation 항목 참조.

name = "Mark"
print(f'Hello, {name}!')

Python 모듈(PIP 패키지)기반 동적 플러그인 개발 기법

간단 요약:

  • pkgutil.iter_modules를 사용하여 패키지 검색.
  • importlib.import_module를 사용하여 패키지 임포트.

종종 Python 응용 프로그램이나 라이브러리를 만들 때 플러그인 을 통해 사용자 지정 또는 추가 기능을 제공하는 기능을 원할 것 입니다. Python 패키지는 별도로 배포할 수 있으므로 응용 프로그램이나 라이브러리 에서 사용 가능한 모든 플러그인 을 자동으로 검색 할 수 있습니다.

자동 플러그인 검색에는 세 가지 주요 접근 방식이 있습니다:

  • 명명 규칙 사용
  • 네임스페이스 패키지 사용
  • 패키지 메타데이터 사용

명명 규칙 사용

import importlib
import pkgutil

discovered_plugins = {
    name: importlib.import_module(name)
    for finder, name, ispkg
    in pkgutil.iter_modules()
    if name.startswith('flask_')
}

가장 추천하는 방법. (디버깅이 가장 편하다)

네임스페이스 패키지 사용

import importlib
import pkgutil

import myapp.plugins

def iter_namespace(ns_pkg):
    # Specifying the second argument (prefix) to iter_modules makes the
    # returned name an absolute name instead of a relative one. This allows
    # import_module to work without having to do additional modification to
    # the name.
    return pkgutil.iter_modules(ns_pkg.__path__, ns_pkg.__name__ + ".")

discovered_plugins = {
    name: importlib.import_module(name)
    for finder, name, ispkg
    in iter_namespace(myapp.plugins)
}

WARNING

네임스페이스 패키지는 복잡한 기능이며 이를 생성하는 여러 가지 방법이 있습니다. 패키징 네임스페이스 패키지 문서 를 읽고 프로젝트의 플러그인에 어떤 접근 방식이 선호되는지 명확하게 문서화하는 것이 좋습니다.

WARNING

이 방법은 PyCharm에서 디버깅시 소스코드 Root 디렉토리 설정에 대한 충돌 오류가 발생될 수 있다.

패키지 메타데이터 사용

setuptools의 setup.py에서 진입점을 설정한다.

setup(
    ...
    entry_points={'myapp.plugins': 'a = myapp_plugin_a'},
    ...
)

등록된 모든 진입점을 검색하고 로드할 수 있습니다:

import sys
if sys.version_info < (3, 10):
    from importlib_metadata import entry_points
else:
    from importlib.metadata import entry_points

discovered_plugins = entry_points(group='myapp.plugins')

WARNING

이 방법은 패키지를 반드시 설치해야 한다.

난독화 (Obfuscation)

배포시 바이너리로 만든다. Cython, Nuitka, Shed Skin 와 같은 도구를 사용하여 C코드를 컴파일한다.

상단의 #난독화 (Obfuscation) 라이브러리 항목도 참조.

Download package

아래와 같은 방법으로 전체 패키지 파일을 획득할 수 있다.

$ pip3 list | awk '{print $1}' | sed -n '3,$p' > requirements
$ pip3 download -r requirements

Python underscore

dunder 항목 참조.

Python gdb debugging

C 레벨의 Runtime Error 가 발생될 경우 정상적인 StackTrace 를 보기 힘들다. 이 경우 Gdb#Python gdb debugging 항목 참조.

Debugging Mode 확인 방법

# -*- coding: utf-8 -*-

import sys


def is_debugging_mode() -> bool:
    """
    Return if the debugger is currently active
    """

    get_trace = getattr(sys, "gettrace", lambda: None)
    return get_trace() is not None

future.annotations.compiler_flag

__future__.annotations.compiler_flag 포터블하게 획득하는 방법:

# -*- coding: utf-8 -*-

from importlib import import_module


def get_annotations_compiler_flag() -> int:
    future = import_module("__future__")
    assert future is not None
    annotations = getattr(future, "annotations")
    assert annotations is not None
    compiler_flag = getattr(annotations, "compiler_flag")
    assert isinstance(compiler_flag, int)
    return compiler_flag

파이썬 악의축 회피를 위한 주의사항

기본 인자에 함수 사용 금지

from datetime import datetime
from time import sleep


def get_update_task_query_by_uid(
    updated_at=datetime.now().astimezone(),
):
    print(updated_at)


get_update_task_query_by_uid()
sleep(1)

get_update_task_query_by_uid()
sleep(1)

get_update_task_query_by_uid()
sleep(1)

get_update_task_query_by_uid()
sleep(1)

위의 코드에서 updated_at 인자는 최초에 datetime.now().astimezone()를 한 번 초기화 되고 그 이후로 호출되지 않는다.

따라서 Optional[datetime] = None 과 같이 사용하는 것이 좋다.

참고로 위 코드를 실행하면 다음과 같이 출력된다.

2021-10-07 16:44:12.581059+09:00
2021-10-07 16:44:12.581059+09:00
2021-10-07 16:44:12.581059+09:00
2021-10-07 16:44:12.581059+09:00

클래스 멤버 초기화는 인스턴스가 아님

클래스 멤버를 초기화 할 경우 이 것을 멤버 초기화로 착각해선 안된다:

class _Test0:
    value = list()

class _Test1(_Test0):
    def __init__(self):
        pass

a = _Test1()
a.value.append(0)
b = _Test1()
b.value.append(1)
b.value.append(2)
c = _Test1()
c.value = [3, 4]
d = _Test1()
d.value.append(5)
_Test1.value.append(100)

print(a) # [0, 1, 2, 5, 100]

이건 POD 데이터 타입도 마찬가지 이다.

class _Test0:
    value = 100

class _Test1(_Test0):
    def __init__(self):
        pass

a = _Test1()
b = _Test1()
print(a.value) # 100
print(b.value) # 100

_Test1.value = 200
print(a.value) # 200
print(b.value) # 200

Mixin 클래스의 자동 초기화

Python:Mixin#Mixin 클래스의 자동 초기화 방법 항목 참조.

Cheat Sheet

Python 3 Cheat Sheet
https://perso.limsi.fr/pointal/_media/python:cours:mementopython3-english.pdf
Memento-python3-english.pdf

인자에 대한 제한

키워드 인자만 가능하도록 제한

위치 인자가 끝나는 지점에 '*'를 붙인다.

def foo(a, *, b):
    print(a, b)

foo(1, 2)  # 에러
foo(1, b=2)  # 유효

위치 인자만 가능하도록 제한

Python에서 함수 정의 시 인자 목록의 끝에 '/'를 붙이는 것은 함수의 매개변수가 위치 인자로만 받아들이도록 강제하는 것입니다. 이것은 Python 3.8에서 새롭게 추가된 문법입니다.

def foo(a, b, /):
    print(a, b)

foo(1, 2)  # 유효한 호출
foo(a=1, b=2)  # 에러: foo()는 위치 인자만 받아들입니다.

"is None" vs "

None" == python 에서 None 비교를 is None 으로 하는데, == None 으로 하지 않는 이유는?

is 연산자
두 객체가 같은 메모리 위치를 참조하는지 확인합니다.
  • 즉, 두 객체가 동일한 객체(identical)인지 확인합니다.
  • 예: x is yid(x) == id(y)와 동일합니다.
== 연산자
두 객체의 값(value)이 같은지를 확인합니다.
  • 이는 객체의 __eq__ 메서드를 호출하여 비교합니다.
  • 따라서 두 객체가 다른 메모리 위치에 있지만 값이 같으면 True가 될 수 있습니다.
  • None은 단일 객체
    • None은 파이썬에서 하나의 전역적인 싱글톤 객체입니다. 즉, None은 프로그램 실행 중 항상 같은 메모리 주소를 가지는 하나의 객체입니다.
    • 따라서 is None을 사용하는 것이 더 직관적이고 정확합니다.
  • is None의 장점
    • 성능: is는 객체의 ID를 단순 비교하기 때문에 더 빠릅니다.
    • 의도 명확성: is None은 "이 객체가 None인지 확인"이라는 명확한 의미를 전달합니다.
    • 반면, == None__eq__를 호출하기 때문에 오버로딩된 메서드의 영향을 받을 수 있어 의도와 다르게 동작할 가능성이 있습니다.

Example

Main module

이것이 뜻하는 의미는 C:/Python> python mod1.py처럼 직접 이 파일을 실행시켰을 때는 __name__ == "__main__"이 참이 되어 if문 다음 문장들이 수행되고,
대화형 인터프리터나 다른 파일에서 이 모듈을 불러서 쓸때는 __name__ == "__main__"이 거짓이 되어 if문 아래문장들이 수행되지 않도록 한다는 뜻이다.

if __name__ == "__main__":
    # TODO: Insert to source code.

Current script name

__main__이 호출된 모듈은 아래와 같이 획득할 수 있다.

import __main__ as main
print(main.__file__)

## Absolute path:
import os
print os.path.abspath(main.__file__)

현재 파일(스크립트)이름을 획득하는 방법은 아래와 같다.

os.path.realpath(__file__)

Encoding setting

스크립트 파일 첫 번째 줄에 파일의 인코딩을 명시해주면 된다.

# -*- coding: cp949 -*-

Method Overload

    @overload
    def get(self, key: _KT) -> Optional[_VT_co]: ...
    @overload
    def get(self, key: _KT, default: Union[_VT_co, _T]) -> Union[_VT_co, _T]: ...

Split key-value tokens

아래와 같은 문자열을 분석하여...

"remove_unknoen=true; rtsp_transport=tcp; defined=key=val;"

아래와 같은 사전형으로 반환한다.

{
    'remove_unknoen': 'true',
    'rtsp_transport': 'tcp',
    'defined': 'key=val'
}

구현된 함수는 아래와 같다.

def split_option(text: str, kv_split='='):
    i = text.find(kv_split)
    if i == -1:
        return None
    return text[0:i].strip(), text[i+1:].strip()

def str_to_dict(text: str, item_split=';', kv_split='='):
    items = [x for x in text.split(item_split)]
    items = list(filter(lambda x: x.strip(), items))
    result = {}
    for x in items:
        opt = split_option(x, kv_split)
        if opt:
            assert len(opt) == 2
            result[opt[0]] = opt[1]
    return result

위의 사전형을 다시 문자열로 합치는 함수는 아래와 같다.

from functools import reduce

def dict_to_str(items: dict, item_split=';', kv_split='='):
    return reduce(lambda x, y: x+item_split+y,
                  [str(k)+kv_split+str(v) for k, v in items.items()])

Appending to Your PYTHONPATH

코드상에서 PYTHONPATH를 추가하는 방법은 아래와 같다.

import sys
sys.path.append("/home/me/mypy") 

적용된 PYTHONPATH목록을 확인하는 방법은 아래와 같다.

import sys
print(sys.path)

사용 예제는 아래와 같다.

import os.path as osp
import sys

def add_path(path):
    if path not in sys.path:
        sys.path.insert(0, path)

this_dir = osp.dirname(__file__)

# Add caffe to PYTHONPATH
caffe_path = osp.join(this_dir, '..', 'python')
add_path(caffe_path)

Python STDOUT Buffer

환경변수 PYTHONUNBUFFERED를 참조.

Check platform

import os
print os.name

## OR
import platform
print platform.platform()

Generics

제네릭 클래스 작성 방법은 Python:typing#Generics 항목 참조.

Checking File Permissions

import os
os.access('my_file', os.R_OK) # Check for read access
os.access('my_file', os.W_OK) # Check for write access
os.access('my_file', os.X_OK) # Check for execution access
os.access('my_file', os.F_OK) # Check for existence of file

Python version which command

def which(program):
    path = os.getenv('PATH')
    for cursor in path.split(os.path.pathsep):
        cursor = os.path.join(cursor, program)
        if os.path.exists(cursor) and os.access(cursor, os.X_OK):
            return cursor
    return ''

Python version variable

import sys
version_info = sys.version_info
version_text = f"{version_info[0]}.{version_info[1]}.{version_info[2]}"
print(version_text)

Elapsed Time Calculator

경과 시간 (Duration) 구하는 방법은 Python:time#Elapsed Time Calculator 항목 참조.

Input

val1 = input("Input : ")
val2 = raw_input("Input : ")

다만 input()을 사용할 경우 이미 선언한 Python 변수명을 사용할 수 있다.

가장 Bytes Copy 방법

밴치마크 코드는 다음과 같다:

import copy
import timeit

original_bytes = b"some bytes data" * 1000

def using_bytes_constructor():
    return bytes(original_bytes)

def using_slicing():
    return original_bytes[:]

def using_bytearray():
    return bytearray(original_bytes)

def using_copy():
    return copy.copy(original_bytes)

def using_memoryview():
    return memoryview(original_bytes).tobytes()

# Benchmark each method
count = 10_000_000
print("Using slicing:", timeit.timeit(using_slicing, number=count))
print("Using bytearray:", timeit.timeit(using_bytearray, number=count))
print("Using copy:", timeit.timeit(using_copy, number=count))
print("Using memoryview:", timeit.timeit(using_memoryview, number=count))
print("Using bytes constructor:", timeit.timeit(using_bytes_constructor, number=count))

AMD Ryzen 7 4700U CPU 2.0GHz 에서 테스트한 결과:

  • Using slicing: 0.7010392720003438
  • Using bytearray: 4.187463062000461
  • Using copy: 2.724746272999255
  • Using memoryview: 4.6049892060000275
  • Using bytes constructor: 1.6053336320001108

결과, Slicing 이 가장 빠르다.

Bytes 와 HEX 문자열 상호 변환

bytes to HEX String

a = bytes([10, 20, 30, 40, 50])
a.hex()

HEX String to bytes

bytes.fromhex("deadbeef")

Integer를 Bytes로 직렬화

data = 9999
serialized_data = data.to_bytes(4, "big", signed=False)
print(serialized_data.hex())  ## 0000270f
result = int.from_bytes(serialized_data, "big", signed=False)
print(result)  ## 9999

Python:struct를 사용한 방식으로 변환하고 싶다면 Python:struct#Integer를 Bytes로 직렬화 항목을 참조.

Current working directory

Return a string representing the current working directory.

os.getcwd()

List of files

import os
import re

def get_child_directories(path=os.curdir):
    result = []
    for parent, sub_dirs, files in os.walk(path):
        result.append(parent)
    return result


def get_children(path=os.curdir):
    result = []
    for parent, sub_dirs, files in os.walk(path):
        for name in files:
            # result.append(os.path.join(os.path.abspath(parent), name))
            result.append(os.path.join(parent, name))
    return result


def get_children_with_match(path=os.curdir, regexp='.*'):
    result = []
    for cursor in get_children(path):
        if not re.match(regexp, cursor) is None:
            result.append(cursor)
    return result


def get_files(path=os.curdir):
    return list(filter(lambda x: os.path.isfile(os.path.join(path, x)), os.listdir(path)))


def get_dirs(path=os.curdir):
    return list(filter(lambda x: os.path.isdir(os.path.join(path, x)), os.listdir(path)))

Command-Line Arguments

import sys
print 'Number of arguments:', len(sys.argv), 'arguments.'
print 'Argument List:', str(sys.argv)

staticmethod vs classmethod

아래와 같이 staticmethod와 classmethod를 추가할 수 있다.

class Object:
    @staticmethod
    def method(content):
        print content

    factor = 1
    @classmethod
    def mul(cls, x):
        return cls.factor*x
  • staticmethod는 C++의 의 그것과 동일합니다. class는 그냥 이름만 빌려준것이고 일종의 namespace로 함수를 구현하는 것이고 instance에서는 실행되지 않습니다.
  • classmethod는 인자로 클래스 자기자신을 받게 되어있습니다. 관습적으로 cls라고 표기를 합니다. 그래서 instance에서 호출을 하면, class을 인자로 넘겨주게 됩니다.

args and kwargs

Python:Function#args and kwargs 항목 참조.

instance check

Type을 검사하는 방법은 아래와 같다.

x = 'string'
isinstance(x, basestring) # Python 2.x, you would do
isinstance(x, str) # In Python 3.x, it would be
isinstance(x, unicode) # In Python 3.x, it would be
isinstance(x, list)
# ...

클래스의 프로퍼티 목록을 출력하는 방법은 아래와 같다.

an = Animal()
dir(a)

클래스의 멤버 변수 목록을 출력하는 방법은 아래와 같다.

an = Animal()
an.__dict__
## or
attrs = vars(an)

access class property from string

문자열로 클래스의 속성을 접근하는 방법은 다음과 같다:

  • getattr(x, 'y') is equivalent to x.y
  • setattr(x, 'y', v) is equivalent to x.y = v
  • delattr(x, 'y') is equivalent to del x.y

Forward declarations (클래스 안에서 동일한 클래스 타입을 참조)

Inside of the class, the class is not defined yet, causing a NameError (and PyCharm to complain).

To get around this, use forward declarations: def foo_method(self, other_foo: "Foo"):
return "Hello World!"

</syntaxhighlight>

Basically, if a type annotations is a string, it is evaled after the whole module is loaded, so it can evaluate to the Foo class.

Execute a string

문자열을 직접 실행하는 방법은 아래와 같다.

exec 'print "hello world"'
## OR:
x = eval("2+2")

Singleton

class SingletonInstane:
  __instance = None

  @classmethod
  def __getInstance(cls):
    return cls.__instance

  @classmethod
  def instance(cls, *args, **kargs):
    cls.__instance = cls(*args, **kargs)
    cls.instance = cls.__getInstance
    return cls.__instance

class MyClass(BaseClass, SingletonInstane):
  pass

c = MyClass.instance()

Environment variables in Python

환경변수 사용방법은 아래와 같다.

import os

# Set environment variables
os.environ['API_USER'] = 'username'
os.environ['API_PASSWORD'] = 'secret'

# Get environment variables
USER = os.getenv('API_USER')
PASSWORD = os.environ.get('API_PASSWORD')

# Getting non-existent keys
FOO = os.getenv('FOO') # None
BAR = os.environ.get('BAR') # None
BAZ = os.environ['BAZ'] # KeyError: key does not exist.

Python install path

파이선 설치 경로는 아래와 같이 간접적으로 확인할 수 있다.

>>> import sys
>>> sys.path
['', '/Users/username/Project/opm/python', '/Library/Python/2.7/site-packages/scons-2.3.0', ... ]

Path of imported module

>>> import sys
>>> sys.modules['os']
<module 'os' from 'c:\Python26\lib\os.pyc'>

Find Python libraries

파이선 라이브러리를 찾는 방법은 해당 항목을 참조.

Python PIPE

import os
import errno

FIFO = 'mypipe'

try:
    os.mkfifo(FIFO)
except OSError as oe: 
    if oe.errno != errno.EEXIST:
        raise

print("Opening FIFO...")
with open(FIFO) as fifo:
    print("FIFO opened")
    while True:
        data = fifo.read()
        if len(data) == 0:
            print("Writer closed")
            break
        print('Read: "{0}"'.format(data))

As others have pointed out, there is no built-in mount function. However, it is easy to create one using ctypes, and this is a bit lighter weight and more reliable than using a shell command.

python 에서 mount 하는 방법

Here's an example:

import ctypes
import ctypes.util
import os

libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True)
libc.mount.argtypes = (ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_ulong, ctypes.c_char_p)

def mount(source, target, fs, options=''):
  ret = libc.mount(source.encode(), target.encode(), fs.encode(), 0, options.encode())
  if ret < 0:
    errno = ctypes.get_errno()
    raise OSError(errno, f"Error mounting {source} ({fs}) on {target} with options '{options}': {os.strerror(errno)}")

mount('/dev/sdb1', '/mnt', 'ext4', 'rw')

Python 에서 User(사용자) 변경 방법

다음 함수들 참조.

  • os.getgid()
  • os.getuid()
  • os.setgid()
  • os.getuid()

또는 Stackoverflow - Changing user within python shell 페이지 참조.

사용자 이름기반으로 UID 취득하고 싶다면 Python:pwd 패키지 참조.

Dict 상속 방법

from typing import Dict

class User(Dict[str, str]):
    pass

소멸자 구현 방법

자세한 내용은 weakref.finalize 항목 참조.

site-packages 확인 방법

다음과 같은 방법이 있다.

  • python -m site
  • python -c 'import site; print(site.getsitepackages())'
  • python3 -c 'import sysconfig; print(sysconfig.get_paths()["purelib"])'
  • python -m site --user-site

User Directory/Data

다음 라이브러리 중 하나를 사용하거나

  • appdirs - A small Python module for determining appropriate platform-specific dirs, e.g. a "user data dir".
  • appdata - AppData is a Python library to access app data paths based on the operating system the app is running on.

예제의 함수를 구현:

import sys
import logging
import pathlib
from typing import Optional, Union, List, Tuple


logger = logging.getLogger(__name__)


def get_user_data_dir(appending_paths: Union[str, List[str], Tuple[str, ...]] = None) \
        -> pathlib.Path:
    """
    Returns a parent directory path where persistent application data can be stored.
    Can also append additional paths to the return value automatically.

    Linux: ~/.local/share
    macOS: ~/Library/Application Support
    Windows: C:/Users/<USER>/AppData/Roaming

    :param appending_paths: Additional path (str) or paths (List[str], Tuple[str]) to append to return value
    :type appending_paths: Un

    :return: User Data Path
    :rtype: str
    """
    logger.debug(f'Getting Home Path...')
    home = pathlib.Path.home()
    logger.debug(f'Home Path: {home}')

    system_paths = {
        'win32': home / 'AppData/Roaming',
        'linux': home / '.local/share',
        'darwin': home / 'Library/Application Support'
    }

    logger.debug(f'Getting System Platform...')
    if sys.platform not in system_paths:
        raise SystemError(f'Unknown System Platform: {sys.platform}. Only supports {", ".join(list(system_paths.keys()))}')
    data_path = system_paths[sys.platform]

    if appending_paths:
        if isinstance(appending_paths, str):
            appending_paths = [appending_paths]
        for path in appending_paths:
            data_path = data_path / path

    logger.debug(f'System Platform: {sys.platform}')
    logger.debug(f'User Data Directory: {system_paths[sys.platform]}')
    logger.debug(f'Return Value: {data_path}')
    return data_path

Read-only property

This will make Foo.number a read-only property:

class MetaFoo(type):
    @property
    def number(cls):
        return cls.x

class Foo(object, metaclass=MetaFoo):
    x = 4

print(Foo.number)
# 4

Foo.number = 6
# AttributeError: can't set attribute

Python 에서 2025년의 데이터 검증 환경

  • 2025년의 데이터 검증 환경(Landscape) | GeekNews
  • 현 시점(2025년)의 주요 데이터 검증 도구에 대한 설명 및 상황 별 추천
  • 데이터 검증(유효성 검사) 은 데이터의 품질을 자동 또는 반자동으로 확인하는 과정임
    • 데이터 유형 확인, 누락된 값 수 확인, 비정상적인 값 탐지
  • 데이터프레임의 행뿐만 아니라 API 입력값이나 폼 제출 값도 검증 가능
  • 사용자는 특정 열의 값이 특정 범위에 있어야 한다는 등의 규칙을 설정 가능
  • 검증 실패 시: 오류 발생, 검증 보고서 생성 후 수동 또는 자동 처리 가능

왜 데이터 검증이 중요한가

  • 공공기관의 분석 작업은 다음 두 가지로 나뉨:
    • 애드혹 분석 – 일회성 분석 작업
    • 정기 통계 생성 – 정기적으로 새로운 데이터 수집 및 처리
  • 데이터 오류가 분석 결과에 영향을 주기 전에 검증이 필요함
  • 데이터 검증은 오류 위험을 줄이고 정확도를 높이는 데 효과적임

주요 데이터 검증 도구

  • Great Expectations - Data Quality 를 위한 오픈소스 프레임워크
  • Pointblank - 2024년 출시된 최신 Python 데이터 검증 도구 (RStudio → Posit 제작)
  • Pandera - Great Expectations와 유사한 API 제공
  • Pydantic - 데이터프레임이 아닌 딕셔너리 기반 검증 도구
  • Cerberus - 딕셔너리 기반 검증 도구
  • jsonschema - JSON 데이터 검증 도구

공공 부문에서 어떤 도구를 사용할 것인가

  • 데이터프레임 또는 데이터베이스 검증:
    • 생산 시스템에서 사용 → Great Expectations 추천
    • 간단한 검증 → Pandera 추천
    • 최신 도구 시도 → Pointblank 추천
  • API나 사용자 입력 검증:
    • 비정형 데이터 → Pydantic 추천
  • 단순 JSON 검증:
    • jsonschema 추천
  • 단순 검증이 필요하면:
    • Cerberus 추천

Troubleshooting

TypeError: 'type' object is not subscriptable

예를 들어, dict[int] 와 같은 타입 인자를 제거하여 dict 와 같이 변경.

TypeError: get() takes no keyword arguments

함수에 사용된 키워드 인자를 제거.

예를 들어, get(size=10)와 같이 사용되었다면 get(10)로 변경.

TypeError: 'method' object is not subscriptable

대괄호[...]와 괄호(...)를 잘 사용하였는지, Type Annotation 을 사용할 수 없는 위치에 사용했는지 등 확인하자.

예를 들어 다음과 같은 코드가 있을 때:

from multiprocessing import SimpleQueue


def _receiver_main(queue: SimpleQueue[bytes]):
    ...

SimpleQueue[bytes]SimpleQueue로 사용해야 한다.

UnicodeDecodeError

import codecs
with codecs.open(file_name, 'r', encoding='utf-8', errors='ignore') as f:
    print(f.read())

Unsafe use of relative rpath

Mac OSX에서 특정 라이브러리를 import할 경우 아래와 같은 현상이 발생할 수 있다.

ImportError: dlopen(/Users/username/Project/fast-rcnn/tools/../caffe-fast-rcnn/python/caffe/_caffe.so, 2): Library not loaded: libboost_system.dylib
  Referenced from: /Users/username/Project/fast-rcnn/libboost_thread.dylib
  Reason: unsafe use of relative rpath libboost_system.dylib in /Users/username/Project/fast-rcnn/libboost_thread.dylib with restricted binary

이 때, 해당 라이브러리의 install name을 변경하여 해결할 수 있지만, 간단한 해결책은 아래와 같이 Python 바이너리를 복사면 된다.

$ cp System/Library/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python .

Runtime error R6034

제시된 방법중 Anaconda를 사용할 경우 아래와 같은 방법도 있다.

$ conda install -c anaconda msvc_runtime=1.0.1

PyThreadState_Get: no current thread

아래와 같은 에러가 출력되면서 프로그램이 종료된다.

Fatal Python error: PyThreadState_Get: no current thread
[kilojoules-20160s:64471] *** Process received signal ***
[kilojoules-20160s:64471] Signal: Abort trap: 6 (6)
[kilojoules-20160s:64471] Signal code:  (0)
[kilojoules-20160s:64471] *** End of error message ***

별개의 라이브러리에서 각각 별개의 Python 구현체를 갖고있을 경우 위와 같은 현상이 발생될 수 있다. 보통 정적 라이브러리로 여러 라이브러리를 만들면 생길 수 있는 문제이다. 공유라이브러리로 바꾸면 된다.

Custom OpenSSL library

여러 OpenSSL 라이브러리가 존재하여, -lssl 플래그가 원하는 경로의 SSL에 링크되지 않을 경우 아래와 같은 해결 방법이 있다.

configure.acModules/Setup.dist 파일을 아래와 같이 수정한다.

diff -urN Python-3.7.3-ori/configure.ac Python-3.7.3/configure.ac
--- Python-3.7.3-ori/configure.ac   2019-03-26 05:21:05.000000000 +0900
+++ Python-3.7.3/configure.ac   2019-06-12 10:13:29.831577474 +0900
@@ -5612,6 +5612,14 @@
 # Check for usable OpenSSL
 AX_CHECK_OPENSSL([have_openssl=yes],[have_openssl=no])

+have_openssl=yes
+OPENSSL_INCLUDES="/usr/local/c2core/include"
+OPENSSL_LDFLAGS="/usr/local/c2core/lib/libssl.so /usr/local/c2core/lib/libcrypto.so"
+OPENSSL_LIBS=""
+AC_SUBST([OPENSSL_INCLUDES])
+AC_SUBST([OPENSSL_LIBS])
+AC_SUBST([OPENSSL_LDFLAGS])
+
 if test "$have_openssl" = yes; then
     AC_MSG_CHECKING([for X509_VERIFY_PARAM_set1_host in libssl])

diff -urN Python-3.7.3-ori/Modules/Setup.dist Python-3.7.3/Modules/Setup.dist
--- Python-3.7.3-ori/Modules/Setup.dist 2019-03-26 05:21:05.000000000 +0900
+++ Python-3.7.3/Modules/Setup.dist 2019-06-12 10:14:53.375633702 +0900
@@ -204,14 +204,14 @@
 #_csv _csv.c

 # Socket module helper for socket(2)
-#_socket socketmodule.c
+_socket socketmodule.c

 # Socket module helper for SSL support; you must comment out the other
 # socket line above, and possibly edit the SSL variable:
-#SSL=/usr/local/ssl
-#_ssl _ssl.c \
-#  -DUSE_SSL -I$(SSL)/include -I$(SSL)/include/openssl \
-#  -L$(SSL)/lib -lssl -lcrypto
+SSL=/usr/local/c2core
+_ssl _ssl.c \
+   -DUSE_SSL -I$(SSL)/include -I$(SSL)/include/openssl \
+   -L$(SSL)/lib $(SSL)/lib/libssl.so $(SSL)/lib/libcrypto.so

 # The crypt module is now disabled by default because it breaks builds
 # on many systems (where -lcrypt is needed), e.g. Linux (I believe).

그 후, 아래와 같이 명령을 실행하면 된다. 중요한 점은 -Wl,-rpath,$PREFIX/lib옵션을 사용하여 공유 라이브러리를 우선 찾는 경로를 지정해야 한다.

PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig \
    CFLAGS="-I/opt/X11/include -I$PREFIX/include -fPIC" \
    LDFLAGS="-L/opt/X11/lib -L$PREFIX/lib -Wl,-rpath,$PREFIX/lib" \
    ./configure --enable-shared --enable-optimizations --prefix=$PREFIX | tee $BUILD_LOG1
make -j8 install

make error: ModuleNotFoundError: Not module named '_ctypes'

ModuleNotFoundError: Not module named '_ctypes'
maek: *** [install] Error 1

소스코드로 빌드시 위와 같은 에러가 발생될 경우 libffi를 설치해 보자. Ubuntu의 경우 sudo apt-get install libffi-dev를 사용하자.

One more word, full necessary dependencies on my Ubuntu 18.04:

sudo apt-get install build-essential libsqlite3-dev sqlite3 bzip2 libbz2-dev zlib1g-dev libssl-dev openssl libgdbm-dev libgdbm-compat-dev liblzma-dev libreadline-dev libncursesw5-dev libffi-dev uuid-dev

CERTIFICATE_VERIFY_FAILED

CERTIFICATE_VERIFY_FAILED항목 참조.

ImportError: dlopen: cannot load any more object with static TLS

해결중 ...

Can't connect to HTTPS URL because the SSL module is not available

Python 빌드시 OpenSSL와 함께 설치해야 한다.

exec 에서 typing 패키지의 NameError 발생

Python:builtins#exec 에서 typing 패키지의 NameError 발생 항목 참조.

exec 이후 coverage.py 오작동 발생

Python:builtins#exec 이후 coverage.py 오작동 발생 항목 참조.

os.getcwd() 에서 FileNotFoundError 가 발생될 경우

Python:os#FileNotFoundError in os.getcwd 항목 참조.

ModuleNotFoundError: No module named '../pydevd'

Python:ensurepip#ModuleNotFoundError in pydevd 항목 참조.

eval 실행 직후 Annotation 누실 문제

Python:Eval#Annotation 누실 문제 항목 참조.

Missing api-ms-win-core-path-l1-1.0.dll

python 3.9.x 이상 부터 Windows 7 에서 지원되지 않는듯 하다. (필요하면 직접 컴파일)

해당 DLL (api-ms-win-core-path-l1-1.0.dll) 은 Windows 10 부터 지원되는 듯 하다. (Windows 8.1 확인 필요)

object.new() takes exactly one argument

다음과 같은 에러 출력:

TypeError: object.__new__() takes exactly one argument (the type to instantiate)

object.__new__()는 type 에 대한 Argument 단 하나만 필요하다는 내용이다. 다음의 코드 부분이 문제라면:

class ModuleOpen(ModuleBase):
    _opened: bool

    def __new__(cls, *args, **kwargs):
        instance = super().__new__(cls, *args, **kwargs)
        instance._opened = False
        return instance

instance = super().__new__(cls)로 수정하면 된다.

map 결과가 zip 에서 루프되지 않음

areas = map(lambda c: contour_area(c, area_oriented), contours)
for contour, area in zip(contours, areas):
    ...

위의 상황에서 areas 는 map 으로 인해 생성된 Generator 이다. 이넘이 python 3.10.x (정확한 버전및 상황은 모름) 에서 루프되지 않는다. 따라서 map()list(map())처럼 list로 감싸야 한다.

See also

Favorite site

Basic

Online Python Coding

Guide

Parsing C++

Tutorials

References