Python의 일반적인 함정
중복 가능성 :
Python 2.x 문제 및 지뢰
오늘 나는 수년 후에 변경 가능한 기본 주장에 다시 물렸다. 필요한 경우가 아니면 일반적으로 변경 가능한 기본 인수를 사용하지 않지만 시간이 지남에 따라 잊어 버린 것 같습니다. 오늘 응용 프로그램에서 PDF 생성 함수의 인수 목록에 tocElements = []를 추가했으며 이제 "pdf 생성"을 호출 할 때마다 "목차"가 점점 길어집니다. :)
피해야 할 사항 목록에 추가해야 할 사항은 무엇입니까?
항상 가져 오기 예를 들어, 같은 방법으로 모듈
from y import x
과import x
하는 다른 모듈로 취급 .어쨌든 반복자가 될 것이므로 목록 대신 범위 를 사용하지 마십시오
range()
. 다음은 실패합니다.myIndexList = [0, 1, 3] isListSorted = myIndexList == range(3) # will fail in 3.0 isListSorted = myIndexList == list(range(3)) # will not
xrange로도 같은 일이 실수로 수행 될 수 있습니다 .
myIndexList == xrange(3)
여러 예외 유형을주의 깊게 포착하십시오.
try: raise KeyError("hmm bug") except KeyError, TypeError: print TypeError
이것은 버그는 아니지만 "hmm bug"를 출력합니다. 두 유형의 예외를 포착하는 것처럼 보이지만 대신 KeyError를 변수 TypeError 로 포착 하고 대신 다음을 사용합니다.
try: raise KeyError("hmm bug") except (KeyError, TypeError): print TypeError
인덱스를 사용하여 시퀀스를 반복하지 마십시오.
하지 마십시오 :
for i in range(len(tab)) :
print tab[i]
하다 :
for elem in tab :
print elem
For 는 대부분의 반복 작업을 자동화합니다.
enumerate
인덱스와 요소가 모두 필요한 경우 사용하십시오 .
for i, elem in enumerate(tab):
print i, elem
True 또는 False 를 확인하기 위해 "=="를 사용할 때주의하십시오.
if (var == True) :
# this will execute if var is True or 1, 1.0, 1L
if (var != True) :
# this will execute if var is neither True nor 1
if (var == False) :
# this will execute if var is False or 0 (or 0.0, 0L, 0j)
if (var == None) :
# only execute if var is None
if var :
# execute if var is a non-empty string/list/dictionary/tuple, non-0, etc
if not var :
# execute if var is "", {}, [], (), 0, None, etc.
if var is True :
# only execute if var is boolean True, not 1
if var is False :
# only execute if var is boolean False, not 0
if var is None :
# same as var == None
할 수 있는지 확인하지 말고 그냥 수행하고 오류를 처리하십시오.
Pythonistas는 일반적으로 "허가보다 용서를 구하는 것이 더 쉽다"고 말합니다.
하지 마십시오 :
if os.path.isfile(file_path) :
file = open(file_path)
else :
# do something
하다 :
try :
file = open(file_path)
except OSError as e:
# do something
또는 Python 2.6+ / 3 :
with open(file_path) as file :
훨씬 더 일반적이기 때문에 훨씬 좋습니다. 거의 모든 것에 "시도 / 제외"를 적용 할 수 있습니다. 이를 방지하기 위해 무엇을해야하는지에 대해 신경 쓸 필요가 없으며, 위험에 처한 오류에 대해서만 신경 쓸 필요가 있습니다.
유형에 대해 확인하지 마십시오
Python은 동적으로 입력되므로 유형을 확인하면 유연성이 떨어집니다. 대신 동작을 확인하여 덕 타이핑을 사용하십시오. 예를 들어, 함수에서 문자열을 예상 한 다음 str ()을 사용하여 문자열의 모든 객체를 변환합니다. 목록을 예상하고 list ()를 사용하여 목록의 이터 러블을 변환합니다.
하지 마십시오 :
def foo(name) :
if isinstance(name, str) :
print name.lower()
def bar(listing) :
if isinstance(listing, list) :
listing.extend((1, 2, 3))
return ", ".join(listing)
하다 :
def foo(name) :
print str(name).lower()
def bar(listing) :
l = list(listing)
l.extend((1, 2, 3))
return ", ".join(l)
마지막 방법을 사용하면 foo는 모든 객체를 허용합니다. Bar는 문자열, 튜플, 집합, 목록 등을 허용합니다. 저렴한 DRY :-)
공백과 탭을 혼용하지 마십시오.
그냥 하지마. 당신은 울 것입니다.
개체 를 첫 번째 부모로 사용
이것은 까다 롭지 만 프로그램이 성장함에 따라 물릴 것입니다. Python 2.x에는 이전 클래스와 새 클래스가 있습니다. 오래된 것들은, 음, 오래되었습니다. 일부 기능이 부족하고 상속으로 인해 어색한 동작을 할 수 있습니다. 사용할 수 있으려면 모든 클래스가 "새 스타일"이어야합니다. 이렇게하려면 "object"에서 상속하도록 만드십시오.
하지 마십시오 :
class Father :
pass
class Child(Father) :
pass
하다 :
class Father(object) :
pass
class Child(Father) :
pass
Python 3.x에서는 모든 클래스가 새로운 스타일이므로 선언 할 수 있습니다 class Father:
.
__init__
메서드 외부에서 클래스 속성을 초기화하지 마십시오.
다른 언어에서 온 사람들은 Java 또는 PHP로 작업을 수행하기 때문에 유혹을 느낍니다. 클래스 이름을 작성한 다음 속성을 나열하고 기본값을 제공합니다. Python에서 작동하는 것처럼 보이지만 생각하는 방식으로 작동하지 않습니다.
이렇게하면 클래스 속성 (정적 속성)이 설정되고 객체 속성을 얻으려고 할 때 비어 있지 않는 한 값이 제공됩니다. 이 경우 클래스 속성을 반환합니다.
이는 두 가지 큰 위험을 의미합니다.
- 클래스 속성이 변경되면 초기 값이 변경됩니다.
- 변경 가능한 객체를 기본값으로 설정하면 인스턴스간에 동일한 객체를 공유하게됩니다.
하지 마십시오 (정적을 원하지 않는 한) :
class Car(object):
color = "red"
wheels = [wheel(), Wheel(), Wheel(), Wheel()]
하다 :
class Car(object):
def __init__(self):
self.color = "red"
self.wheels = [wheel(), Wheel(), Wheel(), Wheel()]
배열 채우기가 필요할 때 다음과 같이 입력하고 싶을 수 있습니다.
>>> a=[[1,2,3,4,5]]*4
그리고 당신이 그것을 볼 때 당신이 기대하는 것을 충분히 줄 것입니다.
>>> from pprint import pprint
>>> pprint(a)
[[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5],
[1, 2, 3, 4, 5]]
그러나 인구의 요소가 별도의 개체가 될 것이라고 기대하지 마십시오.
>>> a[0][0] = 2
>>> pprint(a)
[[2, 2, 3, 4, 5],
[2, 2, 3, 4, 5],
[2, 2, 3, 4, 5],
[2, 2, 3, 4, 5]]
이것이 필요한 것이 아니라면 ...
해결 방법을 언급 할 가치가 있습니다.
a = [[1,2,3,4,5] for _ in range(4)]
Python Language Gotchas-매우 모호한 방식으로 실패하는 것
변경 가능한 기본 인수를 사용합니다.
선행 0은 8 진수를 의미합니다.
09
Python 2.x에서 매우 모호한 구문 오류입니다.슈퍼 클래스 또는 서브 클래스에서 맞춤법 오류가 재정의 된 메서드 이름. 슈퍼 클래스의 맞춤법 오류는 더 심각한데, 하위 클래스가 올바르게 재정의하지 않기 때문입니다.
Python Design Gotchas
인트로 스펙 션에 시간을 투자합니다 (예 : 유형 또는 수퍼 클래스 ID 또는 기타 항목을 자동으로 판별하려는 시도). 첫째, 소스를 읽음으로써 분명합니다. 더 중요한 것은, 이상한 파이썬 인트로 스펙 션에 소요 된 시간은 일반적으로 다형성을 파악하는 데 근본적인 실패를 나타냅니다. SO에 대한 Python 내성 질문의 80 %는 다형성을 얻지 못했습니다.
코드 골프에 시간을 보냅니다. 애플리케이션의 멘탈 모델이 4 개의 키워드 ( "do", "what", "I", "mean")라고해서이를 수행하기 위해 매우 복잡한 내성 데코레이터 기반 프레임 워크를 구축해야한다는 의미는 아닙니다. Python을 사용하면 DRY를 어리석은 수준으로 만들 수 있습니다. SO에 대한 나머지 Python introspection 질문은 골프 연습을 코딩하는 복잡한 문제를 줄이려고합니다.
몽키 패칭.
실제로 표준 라이브러리를 읽지 못하고 바퀴를 재발 명했습니다.
대화 형 형식을 사용하는 Python을 적절한 프로그램과 결합합니다. 대화 형으로 입력하는 동안 변수를 추적하지 못할 수 있으며
globals()
. 또한 입력하는 동안 거의 모든 것이 전역입니다. 적절한 프로그램에서는 절대로 변수를 "추적"하지 않으며 전역적인 것은 없습니다.
기본 인수 변경 :
def foo(bar=[]):
bar.append('baz')
return bar
기본값은 함수가 호출 될 때마다가 아니라 한 번만 평가됩니다. 호출을 반복 foo()
반환 ['baz']
, ['baz', 'baz']
, ['baz', 'baz', 'baz']
, ...
바를 변경하려면 다음과 같이하십시오.
def foo(bar=None):
if bar is None:
bar = []
bar.append('baz')
return bar
또는 최종 인수를 원한다면 :
def foo(bar=[]):
not_bar = bar[:]
not_bar.append('baz')
return not_bar
이것이 일반적인 실수인지는 모르겠지만 Python에는 증가 및 감소 연산자가 없지만 이중 부호가 허용되므로
++i
과
--i
구문 상 올바른 코드이지만 "유용"하거나 예상 할 수있는 작업을 수행하지 않습니다.
표준 라이브러리를 살펴보기 전에 자신의 코드를 롤링합니다. 예를 들어 다음과 같이 작성하십시오.
def repeat_list(items):
while True:
for item in items:
yield item
이것을 사용할 수있는 경우 :
from itertools import cycle
자주 간과되는 모듈 (제외 itertools
)의 예는 다음과 같습니다.
optparse
명령 줄 구문 분석기 생성 용ConfigParser
표준 방식으로 구성 파일 읽기tempfile
임시 파일 생성 및 관리shelve
파이썬 객체를 디스크에 저장하기 위해, 완전한 데이터베이스가 과도 할 때 편리합니다.
고유 식별자로 키워드를 사용하지 마십시오.
또한 항상 사용하지 않는 것이 좋습니다 from somemodule import *
.
C ++에서 온 경우 클래스 정의에 선언 된 변수가 정적이라는 것을 인식하십시오. init 메서드 에서 비 정적 멤버를 초기화 할 수 있습니다 .
예:
class MyClass:
static_member = 1
def __init__(self):
self.non_static_member = random()
아무도 이렇게 말하지 않았다는 사실에 놀랐습니다.
들여 쓰기시 탭과 공백을 혼합합니다.
정말 살인자입니다. 나를 믿어. 특히 실행되는 경우.
기능적 도구를 사용하지 않습니다. 이것은 스타일 관점에서의 실수가 아니라 많은 기능 도구가 C에서 최적화되어 있기 때문에 속도 관점에서의 실수입니다.
다음은 가장 일반적인 예입니다.
temporary = []
for item in itemlist:
temporary.append(somefunction(item))
itemlist = temporary
이를 수행하는 올바른 방법 :
itemlist = map(somefunction, itemlist)
이를 수행하는 올바른 방법 :
itemlist = [somefunction(x) for x in itemlist]
한 번에 모두 사용하지 않고 한 번에 하나씩 사용할 수있는 처리 된 항목 만 필요한 경우 반복 가능한 항목을 사용하여 메모리를 절약하고 속도를 향상시킬 수 있습니다.
# itertools-based iterator
itemiter = itertools.imap(somefunction, itemlist)
# generator expression-based iterator
itemiter = (somefunction(x) for x in itemlist)
Pythonista와 같은 코드 : 관용적 Python
오기 re
및 스트링 매칭 / 변환 완벽 좋은의 전체 정규식 접근하여 문자열 방법은 모든 일반적인 동작 존재 (예를 들어 대소 문자, 간단한 매칭 / 검색 참조).
일반 복사 (할당)는 참조로 이루어 지므로 동일한 객체를 적용하고 삽입하여 컨테이너를 채우면 마지막으로 추가 된 객체에 대한 참조가있는 컨테이너가됩니다.
copy.deepcopy
대신 사용하십시오 .
- 표준 출력에 큰 출력 메시지를 쓰지 마십시오.
- 문자열은 변경 불가능합니다. "+"연산자를 사용하지 않고 str.join () 함수를 사용하여 문자열을 빌드하십시오.
- 그 기사를 읽으십시오 :
마지막 링크는 원래 링크이며이 질문은 중복됩니다.
%s
오류 메시지 에서 포맷터 사용 . 거의 모든 상황에서 %r
사용되어야합니다.
예를 들어 다음과 같은 코드를 상상해보십시오.
try:
get_person(person)
except NoSuchPerson:
logger.error("Person %s not found." %(person))
이 오류를 인쇄했습니다.
오류 : 사람 늑대를 찾을 수 없습니다.
그것은 경우 말해 불가능 person
변수는 문자열 "wolever"
, 유니 코드 문자열 u"wolever"
또는 인스턴스 Person
(한 클래스 __str__
로 정의 된 def __str__(self): return self.name
). 반면 %r
사용 된 경우 세 가지 다른 오류 메시지가 있습니다.
...
logger.error("Person %r not found." %(person))
훨씬 더 유용한 오류를 생성합니다.
오류 : 사람 'wolever'를 찾을 수 없습니다. 오류 : 사람 u'wolever를 찾을 수 없습니다. 오류 : 사람을 찾을 수 없습니다.
이것에 대한 또 다른 좋은 이유는 경로가 복사 / 붙여 넣기가 훨씬 더 쉽다는 것입니다. 상상해보십시오.
try:
stuff = open(path).read()
except IOError:
logger.error("Could not open %s" %(path))
경우 path
이며 some path/with 'strange' "characters"
, 오류 메시지가 될 것입니다 :
오류 : '이상한' "문자"가있는 일부 경로를 열 수 없습니다.
시각적으로 구문 분석하기 어렵고 셸에 복사 / 붙여 넣기가 어렵습니다.
반면, %r
사용되는 경우 오류는 다음과 같습니다.
오류 : '일부 경로 / \'이상한 \ ' "문자"'를 열 수 없습니다.
시각적으로 파싱하기 쉽고 복사하여 붙여 넣기가 더 쉽습니다.
내가 스스로 훈련해야했던 나쁜 습관 X and Y or Z
은 인라인 로직 에 사용 하는 것이었다 .
그것이 Y
진정한 가치가 될 것이라고 100 % 항상 보장 할 수 없다면 , 18 개월 후에 코드가 변경 되더라도 예상치 못한 행동에 대비할 수 있습니다.
고맙게도 이후 버전에서는 Y if X else Z
.
2.6에서는 더 이상 사용되지 않는 메서드 사용을 중단하여 앱이나 스크립트를 Python 3으로 쉽게 변환 할 수 있습니다.
++n
및 --n
C 또는 Java 배경에서 오는 사람들이 예상대로 작동하지 않을 수 있습니다.
++n
양수의 양수, 즉 단순히 n
.
--n
음수의 음수로 간단히 n
.
import this
못생긴 것보다 아름다운 것이 낫습니다.
명시적인 것이 암시적인 것보다 낫습니다.
단순한 것이 복잡한 것보다 낫습니다.
복잡한 것이 복잡한 것보다 낫습니다.
플랫이 중첩보다 낫습니다.
희소가 밀도보다 낫습니다.
가독성이 중요합니다.
특별한 경우는 규칙을 어길만큼 특별하지 않습니다.
실용성이 순결을 능가하지만.
오류는 조용히 전달되지 않아야합니다.
명시 적으로 침묵하지 않는 한.
모호함에도 불구하고 추측하려는 유혹을 거부하십시오.
이를 수행하는 분명한 방법은 하나, 바람직하게는 하나만 있어야합니다.
네덜란드 사람이 아니라면 처음에는 그 방법이 분명하지 않을 수도 있습니다.
지금은 결코하지 않는 것보다 낫습니다.
결코 옳은 것보다 낫지는 않지만지금.
구현이 설명하기 어렵다면 그것은 나쁜 생각입니다.
구현이 설명하기 쉽다면 좋은 생각 일 수 있습니다.
네임 스페이스는 훌륭한 아이디어 중 하나입니다. 더 많은 작업을 수행해 보겠습니다!
import not_this
추악한 코드를 작성하십시오.
암시 적 코드를 작성합니다.
복잡한 코드를 작성하십시오.
중첩 된 코드를 작성합니다.
조밀 한 코드를 작성하십시오.
읽을 수없는 코드를 작성하십시오.
특별한 경우를 작성하십시오.
순결을 위해 노력하십시오.
오류 및 예외를 무시하십시오.
출시하기 전에 최적의 코드를 작성하십시오.
모든 구현에는 순서도가 필요합니다.
네임 스페이스를 사용하지 마십시오.
저는 파이썬도 배우기 시작했고 제가 저지른 가장 큰 실수 중 하나는 C ++ / C # 인덱스 "for"루프를 지속적으로 사용하는 것입니다. 파이썬에는 for (i; i <length; i ++) 유형 루프가 있으며 좋은 이유가 있습니다. 대부분의 경우 동일한 작업을 수행하는 더 좋은 방법이 있습니다.
예 : 목록을 반복하고 선택한 항목의 인덱스를 반환하는 메서드가 있습니다.
for i in range(len(myList)):
if myList[i].selected:
retVal.append(i)
대신 파이썬은 더 우아하고 읽기 쉬운 방식으로 동일한 문제를 해결하는 목록 이해력을 가지고 있습니다.
retVal = [index for index, item in enumerate(myList) if item.selected]
멀티 스레드 Python 애플리케이션과 SMP 가능 머신 (예 : 멀티 코어 CPU가 장착 된 머신)이 애플리케이션에 진정한 병렬성을 도입 할 수있는 이점을 제공한다고 결코 가정하지 마십시오. 바이트 코드 인터프리터 수준에서 애플리케이션을 동기화하는 GIL (Global Interpreter Lock) 때문이 아닐 가능성이 높습니다.
이 (에서 제공하는 것과 같은 예를 들어 래퍼를 통해 (대신 스레드의) 여러 프로세스를 C API 호출에 동시 코드를 넣거나 사용하여 SMP의 활용과 같은 몇 가지 해결 방법입니다 http://www.parallelpython.org는 )하지만 한 경우 Python에서 진정한 멀티 스레딩이 필요합니다. Jython, IronPython 등을 살펴 봐야합니다. (GIL은 CPython 인터프리터의 기능이므로 다른 구현에는 영향을 미치지 않습니다).
Python 3000 FAQ (Artima에서 사용 가능)에 따르면 위의 내용은 여전히 최신 Python 버전을 나타냅니다.
약간의 개인적인 의견이 있지만 다음 을 하지 않는 것이 가장 좋습니다 .
더 이상 사용되지 않는 모듈 사용 (경고 사용)
클래스와 상속을 남용 (정적 언어 레거시의 전형)
명시 적 (반복과 같은 알고리즘을 사용 선언
for
을 이용 대itertools
)표준 lib에서 함수를 다시 구현합니다. "모든 기능이 필요하지 않기 때문입니다."
이를 위해 기능 사용 (이전 Python 버전과의 호환성 감소)
정말로 필요하지 않을 때 메타 클래스를 사용하고 더 일반적으로 너무 "매직"하게 만듭니다.
발전기 사용을 피하십시오
(더 개인적인) 낮은 수준에서 CPython 코드를 미세 최적화하십시오. 알고리즘에 더 많은 시간을 투자 한 다음에서 호출하는 작은 C 공유 라이브러리를 만들어 최적화
ctypes
합니다 (내부 루프에서 5 배의 성능 향상을 얻는 것이 매우 쉽습니다).반복자가 충분할 때 불필요한 목록 사용
필요한 라이브러리를 모두 사용할 수 있기 전에 3.x 용 프로젝트를 직접 코딩하십시오 (이 시점은 지금 약간 논란이 될 수 있습니다!).
기본 변경 가능한 인수와 다소 관련이 있습니다. "누락 된"케이스를 확인하는 방법은 빈 목록이 전달 될 때 차이가 발생합니다.
def func1(toc=None):
if not toc:
toc = []
toc.append('bar')
def func2(toc=None):
if toc is None:
toc = []
toc.append('bar')
def demo(toc, func):
print func.__name__
print ' before:', toc
func(toc)
print ' after:', toc
demo([], func1)
demo([], func2)
출력은 다음과 같습니다.
func1
before: []
after: []
func2
before: []
after: ['bar']
당신은 기본 인자를 언급했습니다 ... 하나는 변경 가능한 기본 인자만큼이나 나쁩니다. 기본값은 None
.
음식을 요리하는 기능을 고려하십시오.
def cook(breakfast="spam"):
arrange_ingredients_for(breakfast)
heat_ingredients_for(breakfast)
serve(breakfast)
에 대한 기본값을 지정하기 때문에 breakfast
다른 함수가 특별한 경우없이 "기본 아침 식사 요리"라고 말하는 것은 불가능합니다.
def order(breakfast=None):
if breakfast is None:
cook()
else:
cook(breakfast)
그러나 기본값으로 cook
사용 하면이를 피할 수 있습니다 None
.
def cook(breakfast=None):
if breakfast is None:
breakfast = "spam"
def order(breakfast=None):
cook(breakfast)
이에 대한 좋은 예는 Django 버그 # 6988 입니다. Django의 캐싱 모듈에는 다음과 같은 "캐시 저장"기능이 있습니다.
def set(key, value, timeout=0):
if timeout == 0:
timeout = settings.DEFAULT_TIMEOUT
_caching_backend.set(key, value, timeout)
But, for the memcached backend, a timeout of 0
means "never timeout"… Which, as you can see, would be impossible to specify.
Don't modify a list while iterating over it.
odd = lambda x : bool(x % 2)
numbers = range(10)
for i in range(len(numbers)):
if odd(numbers[i]):
del numbers[i]
One common suggestion to work around this problem is to iterate over the list in reverse:
for i in range(len(numbers)-1,0,-1):
if odd(numbers[i]):
del numbers[i]
But even better is to use a list comprehension to build a new list to replace the old:
numbers[:] = [n for n in numbers if not odd(n)]
The very first mistake before you even start: don't be afraid of whitespace.
When you show someone a piece of Python code, they are impressed until you tell them that they have to indent correctly. For some reason, most people feel that a language shouldn't force a certain style on them while all of them will indent the code nonetheless.
my_variable = <something>
...
my_varaible = f(my_variable)
...
use my_variable and thinking it contains the result from f, and not the initial value
Python won't warn you in any way that on the second assignment you misspelled the variable name and created a new one.
Common pitfall: default arguments are evaluated once:
def x(a, l=[]):
l.append(a)
return l
print x(1)
print x(2)
prints:
[1]
[1, 2]
i.e. you always get the same list.
Creating a local module with the same name as one from the stdlib. This is almost always done by accident (as reported in this question), but usually results in cryptic error messages.
Promiscuous Exception Handling
This is something that I see a surprising amount in production code and it makes me cringe.
try:
do_something() # do_something can raise a lot errors e.g. files, sockets
except:
pass # who cares we'll just ignore it
Was the exception the one you want suppress, or is it more serious? But there are more subtle cases. That can make you pull your hair out trying to figure out.
try:
foo().bar().baz()
except AttributeError: # baz() may return None or an incompatible *duck type*
handle_no_baz()
The problem is foo or baz could be the culprits too. I think this can be more insidious in that this is idiomatic python where you are checking your types for proper methods. But each method call has chance to return something unexpected and suppress bugs that should be raising exceptions.
Knowing what exceptions a method can throw are not always obvious. For example, urllib and urllib2 use socket which has its own exceptions that percolate up and rear their ugly head when you least expect it.
Exception handling is a productivity boon in handling errors over system level languages like C. But I have found suppressing exceptions improperly can create truly mysterious debugging sessions and take away a major advantage interpreted languages provide.
참고URL : https://stackoverflow.com/questions/1011431/common-pitfalls-in-python
'Nice programing' 카테고리의 다른 글
코드 서명 오류 : 프로비저닝 프로필을 찾을 수 없습니다. (0) | 2020.10.17 |
---|---|
이 프로그램은 왜 "forked!"를 인쇄합니까? (0) | 2020.10.17 |
하위 쿼리를 조건으로 사용하는 MySQL DELETE FROM (0) | 2020.10.17 |
pip 설치 /usr/local/opt/python/bin/python2.7 : 잘못된 인터프리터 : 해당 파일 또는 디렉토리 없음 (0) | 2020.10.17 |
신청서 창을 앞으로 가져 오려면 어떻게해야합니까? (0) | 2020.10.17 |