파이썬 재고 관리 프로그램 | 혹시 재고관리를 어떻게 하고계신가요? 안하고 계시진 않으신가요? 재고관리 프로그램이 필요하시면 다운로드받으세요. 273 개의 가장 정확한 답변

당신은 주제를 찾고 있습니까 “파이썬 재고 관리 프로그램 – 혹시 재고관리를 어떻게 하고계신가요? 안하고 계시진 않으신가요? 재고관리 프로그램이 필요하시면 다운로드받으세요.“? 다음 카테고리의 웹사이트 you.charoenmotorcycles.com 에서 귀하의 모든 질문에 답변해 드립니다: https://you.charoenmotorcycles.com/blog/. 바로 아래에서 답을 찾을 수 있습니다. 작성자 퍼스트리 이(가) 작성한 기사에는 조회수 43,513회 및 좋아요 939개 개의 좋아요가 있습니다.

Table of Contents

파이썬 재고 관리 프로그램 주제에 대한 동영상 보기

여기에서 이 주제에 대한 비디오를 시청하십시오. 주의 깊게 살펴보고 읽고 있는 내용에 대한 피드백을 제공하세요!

d여기에서 혹시 재고관리를 어떻게 하고계신가요? 안하고 계시진 않으신가요? 재고관리 프로그램이 필요하시면 다운로드받으세요. – 파이썬 재고 관리 프로그램 주제에 대한 세부정보를 참조하세요

파이썬 재고 관리 프로그램 주제에 대한 자세한 내용은 여기를 참조하세요.

파이썬 판매 관리 프로그램 (물품 입력, 팔린 수량, 전체 판매량)

문제 내용. 팔린 물품과 수량을 입력받고, 다음을 계산하라. – 총 판매 품목 수. – 총 판매 수량. – 각 품목의 판매 수량. – 판매 품목의 순위.

+ 여기에 더 보기

Source: codeuniv.tistory.com

Date Published: 5/6/2021

View: 9899

Python :: 8 – 연관된 데이터를 딕셔너리로 짝을 짓자

그렇다면 이번엔 딕셔너리를 이용해 간단한 예제 코드를 만들어보자. 편의점 재고 관리를 수행하는 프로그램을 작성할텐데,. 이를 위해 편의점에서 판매 …

+ 여기를 클릭

Source: twix.tistory.com

Date Published: 7/16/2021

View: 6941

‘파이썬(Python)/DIY 미니 ERP 만들기’ 카테고리의 글 목록 (3 …

내가 원하는 프로그램만 있다면 내 업무가 좀 더 완벽하게, 단시간 내에, … 재고관리, 물류관리 및 정산과 세무신고를 위한 회계관리까지 아우르는 …

+ 여기에 더 보기

Source: pdatinmylife.tistory.com

Date Published: 8/6/2021

View: 1630

프로젝트 기획안 : [연구실 재고관리 프로그램] – Developer

히스토리페이지 및 대시보드(사진에 없음)는 생각보다 어려울 것 같아서 마지막에 할 계획입니다. ​. 현재 파이썬 장고 프레임워크를 이용해서. ​.

+ 여기에 보기

Source: woongbin96.tistory.com

Date Published: 1/7/2021

View: 1100

파이썬 Python #23 파이썬리스트와 딕셔너리 2- 운석 피하기 …

편의점에서 재고 관리를 수행하는 프로그램을 작성해봅시다. 판매하는 물건의 재고를 딕셔너리에 저장하고, 물건을 입력받아 수량을 출력해봅시다.

+ 여기에 더 보기

Source: jobpedia.tistory.com

Date Published: 9/12/2022

View: 790

[python] 도서 관리 프로그램 – 파일 입출력 포함 – 언제나 휴일

시나리오 도서 관리 프로그램은 콘솔 응용 프로그램이다. 응용에서는 사용자와 상호작용을 담당한다. 응용은 사용자에게 메뉴를 보여주고 선택한 메뉴 …

+ 더 읽기

Source: ehclub.co.kr

Date Published: 12/15/2022

View: 3803

[데이터 관리 프로그램/Python] What’s in my Refrigerator

What’s in my Refrigerator는 냉장고 속 식재료 데이터를 관리하는 프로그램입니다. Heap 자료구조와 파이썬을 이용하여 구현하였습니다.

+ 여기에 더 보기

Source: seungyooon.tistory.com

Date Published: 10/22/2022

View: 4764

PYTHON 9장 리스트와 딕셔너리

PYTHON 9장 리스트와 딕셔너리. 너래쟁이 2018. … 재고를 증가시키는 프로그램을 작성해보시오. 예). 재고 증가 물건의 이름을 입력하시오: 종이컵.

+ 여기를 클릭

Source: kkk-kkk.tistory.com

Date Published: 5/9/2021

View: 8567

주제와 관련된 이미지 파이썬 재고 관리 프로그램

주제와 관련된 더 많은 사진을 참조하십시오 혹시 재고관리를 어떻게 하고계신가요? 안하고 계시진 않으신가요? 재고관리 프로그램이 필요하시면 다운로드받으세요.. 댓글에서 더 많은 관련 이미지를 보거나 필요한 경우 더 많은 관련 기사를 볼 수 있습니다.

혹시 재고관리를 어떻게 하고계신가요? 안하고 계시진 않으신가요? 재고관리 프로그램이 필요하시면 다운로드받으세요.
혹시 재고관리를 어떻게 하고계신가요? 안하고 계시진 않으신가요? 재고관리 프로그램이 필요하시면 다운로드받으세요.

주제에 대한 기사 평가 파이썬 재고 관리 프로그램

  • Author: 퍼스트리
  • Views: 조회수 43,513회
  • Likes: 좋아요 939개
  • Date Published: 2020. 7. 1.
  • Video Url link: https://www.youtube.com/watch?v=mTt9watgK5U

파이썬 판매 관리 프로그램 (물품 입력, 팔린 수량, 전체 판매량)

문제 내용

팔린 물품과 수량을 입력받고, 다음을 계산하라

– 총 판매 품목 수

– 총 판매 수량

– 각 품목의 판매 수량

– 판매 품목의 순위

문제 풀이

product = “temp” dic = {} while product != “”: name = input(“팔린 물품을 입력하시오 (종료: Enter):”) if name == “”: break count = int(input(“팔린 수량을 입력하세요:”)) print(“-“*40) if name in dic: dic[name] += count else: dic[name] = count print(” “) print(“*”*40) print(“총 판매 품목 수 :”,len(dic.keys())) print(“총 판매 수량 :”,sum(dic.values())) print(“*”*40,end=”

판매 순위

“) temp = {} List = [] for k,v in dic.items(): temp[v] = k List.append(v) List.sort() List.reverse() for i in range(len(List)): print(“%d위:\t%s %d개”%(i+1,temp[List[i]],dic[temp[List[i]]]))

코드 결과

#예시 팔린 물품을 입력하시오 (종료: Enter):아메리카노 팔린 수량을 입력하세요:5 —————————————- 팔린 물품을 입력하시오 (종료: Enter):그린티 팔린 수량을 입력하세요:2 —————————————- 팔린 물품을 입력하시오 (종료: Enter):밀크티 팔린 수량을 입력하세요:26 —————————————- 팔린 물품을 입력하시오 (종료: Enter):아메리카노 팔린 수량을 입력하세요:4 —————————————- 팔린 물품을 입력하시오 (종료: Enter):아메리카노 팔린 수량을 입력하세요:3 —————————————- 팔린 물품을 입력하시오 (종료: Enter):그린티 팔린 수량을 입력하세요:17 —————————————- 팔린 물품을 입력하시오 (종료: Enter): **************************************** 총 판매 품목 수 : 3 총 판매 수량 : 57 **************************************** 판매 순위 1위: 밀크티 26개 2위: 그린티 19개 3위: 아메리카노 12개

Python :: 8 – 연관된 데이터를 딕셔너리로 짝을 짓자

728×90

반응형

이전 장에서는 리스트와 튜플이라는 자료형에 대해 알아보았다.

이번에는 키를 이용해서 값을 추출할 수 있는 자료형인 “딕셔너리 dictionary” 에 대해서 알아보자.

주로 데이터베이스에서 흔히 볼 수 있는 구조인데,

딕셔너리는 값 value 과 관련된 키 key 가 있다는 것이 큰 특징이다.

딕셔너리를 만들 때는 중괄호 {} 를 사용한다.

간단하게 전화번호부를 만든다고 가정하고 딕셔너리에 대해 살펴보자.

phone_book = {} # 공백 딕셔너리를 생성 phone_book[“홍길동”] = “010-1234-5678” print(phone_book) # {‘홍길동’: ‘010-1234-5678’}

위의 코드를 보면 {} 를 이용하여 공백 딕셔너리를 만든다.

이름을 key 로 사용하고 value 에는 그 사람의 전화번호를 입력한다.

그러고 나서 딕셔너리를 출력해보면, 중괄호로 감싸져서 키와 값이 출력되는 것을 볼 수 있다.

위와 동일한 방식으로 딕셔너리에 여러 사람들의 전화번호를 추가할 수도 있다.

phone_book = {} # 공백 딕셔너리를 생성 phone_book[“홍길동”] = “010-1234-5678” phone_book[“강감찬”] = “010-1234-6789” phone_book[“이순신”] = “010-1234-4567” print(phone_book) # {‘홍길동’: ‘010-1234-5678’, ‘강감찬’: ‘010-1234-6789’, ‘이순신’: ‘010-1234-4567’}

딕셔너리에서 가장 중요한 연산은 바로 “키를 가지고 연관된 값을 찾는 것”이다.

이는 다음과 같은 코드로 가능하다.

print(phone_book[“강감찬”]) # 010-1234-6789

리스트에서는 인덱스를 가지고 항목을 찾을 수 있지만, 딕셔너리는 키를 가지고 값을 찾는다는 것이 다르다.

딕셔너리에서 사용되는 모든 키를 출력하려면 keys() 메소드를 사용하고,

모든 값을 출력하려면 values() 메소드를 사용한다.

print(phone_book.keys()) # dict_keys([‘홍길동’, ‘강감찬’, ‘이순신’]) print(phone_book.values()) # dict_values([‘010-1234-5678’, ‘010-1234-6789’, ‘010-1234-4567’])

그리고 딕셔너리 내부의 모든 값을 출력하려면 items() 라는 메소드를 사용하면 된다.

items() 메소드를 사용하면 (key, value) 튜플 형태로 데이터가 반환되므로 아래와 같이 활용할 수도 있다.

for name, phone_num in phone_book.items(): print(name, ‘:’, phone_num) # 홍길동 : 010-1234-5678 # 강감찬 : 010-1234-6789 # 이순신 : 010-1234-4567

앞선 장에서는 리스트의 여러 메소드들을 살펴보았는데, 이번에는 딕셔너리 자료형의 메소드들을 살펴보자.

메소드 하는 일 keys() 딕셔너리 내의 모든 키를 반환한다. values() 딕셔너리 내의 모든 값을 반환한다. items() 딕셔너리 내의 모든 항목을 [키] : [값] 쌍으로 반환한다. get(key) 키에 대한 값을 반환, 키가 없으면 None 을 반환한다. pop(key) 키에 대한 값을 반환, 그 항목을 삭제한다. 키가 없으면 KeyError 예외 발생. popitem() 제일 마지막에 입력된 항목을 반환하고, 그 항목을 삭제한다. clear() 딕셔너리 내의 모든 항목을 삭제한다.

이번에는 람다 함수에 대해서 살펴보자.

람다 함수란 “이름이 없는 함수”이다.

함수에 대해서 잘 모르겠다면 아래 게시글을 참고하자.

https://twix.tistory.com/11

람다 함수는 간단한 1회용 작업에 유용하다.

필요한 기능을 함수로 정의해 두면, 필요할 때마다 호출해서 사용하는 것이 편리한데

가끔씩은 함수를 만들지 않고 함수하된 기능만을 불러 사용하고자 할 경우가 있다.

이럴 때는 람다 함수를 이용하면 된다.

람다 함수는 이름이 없기 때문에 재사용이 불가능하다고 생각할 수 있는데 그건 아니다.

이름이 필요할 경우에는 표현식에 할당문을 사용하여 재사용할 수 있다.

람다 함수를 사용할 때 주의할 점은 표현식 안에서 새로운 변수를 선언할 수 없다는 것이다.

그리고 람다 함수의 반환 값은 변수 없이 식 한 줄로 표현할 수 있어야 하기 때문에,

복잡한 기능의 함수는 def 로 정의하여 쓰는 것이 좋다.

두 수를 입력받아 두 수의 합을 반환하는 람다 함수를 만들어보자.

일반 함수는 다음과 같이 정의할 수 있을 것이다.

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

이에 비해 람다 함수는 다음과 같이 쓸 수 있다.

lambda x,y : x + y

이 람다 함수를 이용해서 바로 두 수의 합을 출력해보자.

print(‘100과 200의 합은 :’, (lambda x,y : x + y)(100, 200)) # 300

람다 함수를 이용해서 특정 튜플에서 첫 항목만을 추출하는 함수를 정의할 수 있다.

코드는 다음과 같다.

t = (100, 200, 300) print((lambda x: x[0])(t)) # 100

그렇다면 이번엔 딕셔너리를 이용해 간단한 예제 코드를 만들어보자.

편의점 재고 관리를 수행하는 프로그램을 작성할텐데,

이를 위해 편의점에서 판매하는 물건의 재고를 딕셔너리에 저장한다.

그리고 사용자로부터 물건의 이름을 입력받아서 물건의 재고를 출력하는 프로그램을 작성해보자.

초보자의 입장에서 간단하게 문제를 해결해야 하기 때문에 아주 작은 편의점이라고 가정해보자 🙂

취급하는 물건은 다음과 같다.

items = { “커피음료”: 7, “펜”: 3, “종이컵”: 2, “우유”: 1, “콜라”: 4, “책”: 5 }

input() 문을 사용하여 물건의 이름을 사용자로부터 입력받고, items[물건의 이름] 을 통해서

물건의 재고를 조회하고 출력하면 된다.

결과는 다음과 같이 나와야 한다.

먼저 해답 코드를 보지 말고 코딩해보자.

해답 코드는 생각보다 간다하다. 다음 코드를 보자.

items = { “커피음료”: 7, “펜”: 3, “종이컵”: 2, “우유”: 1, “콜라”: 4, “책”: 5 } name = input(“물건의 이름을 입력하시오: “) print(‘재고 :’, items[name])

그렇다면 이번엔 영한 사전을 만들어보자.

문제를 많이 겪어보며 프로그램을 짜볼수록 실력은 늘게 된다.

문제는 다음과 같다.

영한 사전과 같이 영어 단어를 주면, 이에 해당하는 우리말 단어를 알 수 있게 하려고 한다.

딕셔너리 구조를 활용하여 단어를 입력하고, 검색할 수 있는 프로그램을 만들어보자.

실행된 결과는 명령 프롬프트 ‘$’ 가 나타나고, 입력 명령은 ‘<', 검색 명령은 '>‘ 로 표현하자.

입력은 “영어 단어 : 우리말 단어” 형태로 이루어지며, 검색은 영어 단어를 입력한다.

프로그램의 종료는 ‘q’ 키를 입력하자.

물론 지금까지 공부한 것을 바탕으로 프로그램을 짜기엔 조금 힘들 수 있다.

계속해서 배워나갈 내용들이 포함되어 있지만, 고민해보는 시간만큼 성장할 것이다.

다음은 vscode 를 이용해 해당 프로그램을 실행한 화면이다.

힌트가 있다면, while True : 를 이용해 무한루프를 돌리고, 사용자가 ‘q’ 를 입력할 때까지 반복시킨다.

입력 받은 시작 문자가 < 이면 입력문 s 를 받아 s.split(':') 를 통해서 입력문자를 구분할 수 있다. 사용자의 입력을 받으면 단어의 앞이나 뒤에 의미 없는 공백 문자가 입력될 수도 있다. strip() 함수는 입력 문자열의 앞 뒤에 있는 공백을 깨끗하게 정리해준다. 입력된 문자열을 키:값 구조의 딕셔너리에 넣어서 > 가 입력될 때

이 값을 키로 하여 조회하도록 하자.

문자열을 다루는 함수들은 처음 보는 것이기 때문에 생소할 수 있다.

이 부분은 아래 게시글을 참고하면 된다.

https://twix.tistory.com/14

그러면 이제 해답 코드를 보자.

이때까지의 프로그램들보다 해결하기 어려웠을 수도 있고, 해결을 하지 못 했을 수도 있다.

하지만 괜찮다. 해결하는 것이 중요하지 않다.

고민하는 시간이 중요하다.

print(“사전 프로그램 시작… 종료는 q를 입력”) dictionary = {} while True: st = input(‘$ ‘) command = st[0] # 첫 입력 문자를 추출한다 if command == ‘<': st = st[1:] inputStr = st.split(':') if len(inputStr) < 2 : print('입력 오류가 발생했습니다.') else: dictionary[inputStr[0].strip()] = inputStr[1].strip() elif command == '>‘: st = st[1:] inputStr = st.strip() if inputStr in dictionary: print(dictionary[inputStr]) else : print(‘{}가 사전에 없습니다.’.format(inputStr)) elif command == ‘q’: break else : print(‘입력 오류가 발생했습니다.’) print(“사전 프로그램을 종료합니다.”)

앞에서 배웠던 반복문, 조건문, 리스트, 슬라이싱, 딕셔너리 등

다양한 개념들이 동시에 사용된다.

정답이 꼭 이 코드와 똑같지 않을 수 있다. 코드를 비교해보며 어떻게 코드를 짜는 것이

가장 좋을 지 고민해보는 시간이 중요하다.

이번 장에서는 딕셔너리에 대해서 알아보았다. 뿐만 아니라 조금 어려운 문제를 도전하면서

프로그래밍적인 사고를 키워보기도 했다.

프로그래밍을 처음 공부하는 입장에선 부담스러울 수 있지만,

앞으로 더 공부를 하고 파이썬을 이용해 데이터과학과 머신러닝을 접하게 된다면 훨씬 어려워질 것이다.

그렇기 때문에 작은 문제를 해결하는 것부터 연습하며 기초를 다져야 한다는 것을 느꼈다.

다양한 예제들을 접해보며 실력을 키워나가자 🙂

출처 : 따라하며 배우는 파이썬과 데이터 과학 – 천인국, 박동규, 강영민

728×90

DIY 미니 ERP 만들기’ 카테고리의 글 목록 (3 Page)

Intro

파이썬이라는 언어를 처음 익히기 시작할 때 즈음엔 정상적으로 구동되는 .exe 확장자로 표시되는 우리가 흔히 아는 컴퓨터 프로그램 만들기는 그저 꿈이었습니다. 당장 책에서 설명하는 자료형이란 무엇이고 메서드네 모듈이네 하는 용어부터 메모리는 어떻게 차지하는 것이고 하는 말들도 굉장히 낯설었으니까요. 정말 십여년 직장생활을 통해 그저 관세 무역에 관한 업무만 하였다 보니 IT 창작능력이란게 배양될 환경은 아니었습니다.

그럼에도 제 구미에 맞는 프로그램을 갖고 싶은 욕심은 늘 있었던 것 같습니다. 내가 원하는 프로그램만 있다면 내 업무가 좀 더 완벽하게, 단시간 내에, 일정한 주기로, 정해진 기준에 따라, 정확히 완결될 수 있음은 기성 ERP를 보면 알 수 있었으니깐요.

제 업무의 일부는 기성 ERP의 일부를 사용하는 과정이 포함되어 있었습니다. 제가 경험을 가진 기성 ERP는 SAP 입니다. 아마 많은 분들께서 들어 보셨을 듯 합니다. 꽤 유명한 독일산 ERP.. 심지어, ERP의 제대로된 인터페이스도 본 적 없었던 제가 SAP도입과정에 참여하게 됨으로서 단시간에 ERP의 개념과 작동방식을 들여다 볼 수 있는 다양한 자료를 접할 수 있는 기회를 가졌었습니다. 참으로 고귀한 경험이라 아니할 수 없는게, 통상의 경우에는 이미 도입되어 운용되고 있는 ERP를 선임자로부터 특정 역할 부분만 인계 받아 처리법을 익히게 되기 때문에 그 기초개념부터 이해하고 쓰는 경우는 많다고 할 수가 없습니다. 그래서 늘 하던 업무부분에서 SAP가 특정한 에러코드를 내놓으면 그게 무슨 의미인지 몰라서 헤메다, 많은 시간을 허비한 후에야 해결법에 도달하는 경우도 더러 생겨 나는 듯 합니다.

SAP 초급자 일지언정 흐름상 이런 메시지의 에러코드라면 이 부분이 문제겠구나 하는 귀납적 추론법을 써서 해결법을 찾아나가는데요. 귀납적 추론법이 먹히는 부분은 다양하게 축적된 데이터량이 있는 구간까지 입니다. 그 데이터들로부터 일정 패턴을 찾아내면 해결법은 바로 그 패턴으로부터 얻어내지는 방식이 귀납추론방식이기 때문입니다. 그런데, 기존 데이터가 없거나 그 양이 매우 적어 패턴을 찾기 어려운 경우라면 어떨까요?

예를 들어본다면,

‘막 생산된 재고가 창고로의 재고 적치를 위한 바코드 스캐닝을 하면 해당 정보가 읽혀 서버로 심어져 들어가야 하는데 이상한 에러코드가 뜬다. 이런 경우가 가끔 발생된다. 무엇이 문제일까? 여태껏 이런 경우가 발생했을 때엔 생산 직후 위치 이동과정에서 스캐닝 에러가 생긴 경우 이런 현상이 발생되더라..’

여기까지가 문제가 발생되었을 때, 지난 축적된 경험데이터를 통해 문제의 원인을 파악해 나가는 귀납적 추론의 구간입니다. 누구나 여기까지 도달은 쉽게 하는 것 같습니다. 자기 업무는 늘 겪어왔기 때문이니깐요.

‘그런데, 이번에는 그와는 에러코드가 다르다..정말 처음 접하는 에러코드다..’

위와 같이 처음 접하는 에러코드가 발생되면 문제를 당면한 사용자는 연역적 추론법으로 태세를 전환 해야 합니다. 기존 경험 데이터가 전혀 없기 때문에 귀납추론이 불가하기 때문입니다. ERP 제작자가 정해진 논리에 따라 연산하여 작동하는 시스템이 지금 어떤 연산 부분을 지나고 있는가에 대한 이해는 trouble shooting을 위해서 매우 필요합니다. 바로 이런 논리 이해는 연역추론의 논리뼈대를 이루어 해결법에 도달하게 해주기 때문입니다. 그래서 시스템의 기본적 작동방식과 전후단계 및 가능하다면 처음부터 끝까지 얽혀 들어 있는 각 모듈간의 데이터흐름에 대한 정확한 이해는 SAP 중상급자와 초급자를 가르는 중요한 요소가 되는 것 같습니다.

그렇기에 SAP라고는 본 적도 없던 제가 그 수많은 자료를 통해 아직 제대로 도입되어 작동하고 있지도 않은 상태의 ERP의 작동흐름부터 익히고 도입회사에 맞게 일부 customizing되는 과정에 참여하게 되었다는 점은 정말 값진 경험이었습니다. 그리고 이 경험이 이번 DIY 미니 ERP를 직접 제작해 보는데 있어 어떤 포인트를 챙겨 나가야 하는가에 대한 넓은 이해를 주었기에 빠른 기간내에 초기버전 완성을 성공할 수 있었던 듯 합니다.

파이썬의 여러 모듈에 대한 이해도나 문법사용에 관한 성숙도가 초급은 벗어난 수준까지 올라간 상태에서 진행해 본 개발 프로젝트라서 본디 제작에 두 달은 족히 걸릴 거라 예상했지만 실제 진행해 보니 보름 정도 밖에 걸리지 않았습니다.

제가 제작한 GUI를 갖춘 최초의 완성형 프로그램으로 환율, 과세가격안분 및 용적중량계산기인 AlloCal과 수출입신고서 작성 자동화 프로그램인 AutoCC, 구매주문서와 Commercial Invoice를 연계 발행해 주는 JGPO_Manager 그리고 수출입관계된 회계지표를 시각화해주는 JG Data Center를 제작해 본 이후로 이것이 다섯번째인데요.

AutoCal은 구조적으로 매우 간단한 것임에도 처음 만들어 보는 것이라 3주는 걸렸던 것으로 기억합니다. AutoCC는 데이터 핸들링에서 극도로 정교함이 요구되는 프로그램이기에 여전히 Building과정중이지만 GUI까지 갖추고 실무에 써먹는데 쓸 수 있는 수준으로 만들기까지는 두달은 걸렸습니다.

이전 제작 경험들을 토대로, 파이썬 사용 성숙도가 올라왔을지언정 프로그램의 스케일을 감안해 보았을 때 한달은 걸릴 거라 예상했는데 보름 정도로 공기 단축이 가능했는데요.

제가 파이썬을 너무나도 사랑하는 이유가 바로 이런데 있는 것 같습니다.

방대한 라이브러리를 통해 원하면 지원되는 모듈을 import 해와서 작성할 수 있다는 것 자체가 초보 개발자에게는 천국과 같은 환경입니다. 제가 모든 코드를 짤 필요가 없습니다. 그저 import만으로 무결성을 갖춘 라이브러리를 활용해 뚝딱뚝딱 만들어지기 때문입니다.

저처럼 IT비전공자이면서 초보개발자로서 프로그램 만들기에 도전 중이신 분들께는 파이썬이라는 언어를 다시 한번 강추 드리는 바입니다. 내 전문분야의 실무에 내가 짜놓은 논리데로 움직여 주는 프로그램을 언제든 만들어 낼 수 있게 되신다면 또다른 신세계를 접하시게 되실 거라 확언 드릴 수 있습니다.

또한, 이번 프로젝트는 기존 제작 프로그램들이 관세, 통관 및 무역과 법규준수 관련 제 전문분야에 치중해 제작한 것인 반면, 이번에는 1개의 소규모회사가 갖추어야 할 기능적 요소인 구매(조달), 재고관리, 물류관리 및 정산과 세무신고를 위한 회계관리까지 아우르는 형태로 만든 것이라 제게 있어서도 의미가 좀 남다르긴 합니다. 앞으로 사용해 가면서 유저라고는 아직은 저 혼자 뿐이지만 저의 사용경험을 토대로 버전업을 해나가면서 24시간 일하는 직원으로서 이 프로그램을 막 부릴 생각이기에 매우 중요한 녀석이 될 것 같습니다.

DIY 미니 ERP (프로그램명 미정) 실행화면 일부. 개인정보와 영업비밀보호를 위해 blur 및 painting 처리하였습니다. 양해 부탁 드립니다.

저는 이 카테고리에 제가 DIY 미니 ERP를 개발하면서 느꼈던 어려운 점이나 파이썬 라이브러리별 사용상의 주의점, 직접 개발하실 경우 접하시게 될 여러가지 상황에서 어떤 논리로, 어떤 계획으로, 어떤 코딩으로 그 상황을 해결해 나갔는지 그 경험을 남겨 향후 참조로 쓰고자 하며, 저처럼 IT비전공인 초급자 프로그램 자체개발자 분들에게 제 경험을 공유드리고자 합니다.

제 포스팅 읽어 보신 후 생기시는 의문이나 의견공유, 정정 특히, 더 나은 방식으로의 개발관련 조언은 언제나 환영이오니, 언제든 댓글 남겨주세요.

감사합니다!

반응형

프로젝트 기획안 : [연구실 재고관리 프로그램]

반응형

1. 프로젝트 선정

재고관리는 대부분 공장에서나 일반 가게에서는 제품들이 한정이 되어 있기 때문에, 어떠한 재고관리 프로그램들을 사용해도

크게 불편함을 느끼지는 못하지만, 연구소같이 신제품(시약)들이 계속 쏟아져 나온다면 그만큼 재고관리가 힘들어진다고 생각합니다.

그래서 연구실 재고관리 프로그램을 소프트웨어적으로 웹을 구성하고, 하드웨어적으로도 서비스를 제공하면 어떨까 생각하게 되었습니다.

2. 웹 구성

첫번째로는, 메인홈화면을 만들 생각입니다.

맨 좌측상단에는, 로고를 만들어서 버튼식으로 해당로고를 클릭하면 홈화면으로 이동하는 URL을 추가

그 옆으로는 네비게이션 바를 이용해서 로그인 버튼을 만들예정이고, 로그인 페이지로 이동하면 로그인 및 회원가입을 구성해서 연동할 수 있게 하려고 합니다. (로그인화면과 회원가입 화면은 그리지 못했습니다.)

*별도로 이 프로젝트를 쓰는 연구소 IP만 들어올 수 있게 설정하고 싶은데 그 방법은 아직 모르겠습니다.

로고 밑으로는 또 다른 네비게이션 바를 이용해서 메뉴를 구축할 예정입니다.

제품목록,입고,출고,히스토리 메뉴를 만들예정이고 각 메뉴에 맞는 아이콘들을 좌측에 삽입할 예정입니다.

———————– base.html————————————

위 사항들을 base로 지정하여 코드를 간소화하고 싶습니다. (다른 페이지에서도 동일하게 사용/ 로그인 로그아웃 표시만 다름!)

그리고 홈화면 에는 사진에서처럼 연구하는 이미지와 간단코멘트를 삽입하고

그 밑 부분에는 하드웨어적 서비스페이지랑 연결할 수 있는 이미지파일을 넣고 싶습니다.

홈 화면이랑 연결된 하드웨어적 서비스 제공 페이지는

배경으로 이미지 넣고 제공할 서비스들을 코멘트로 작성할 계획입니다.

메인 홈화면에서만 우측상단에 로그인 표시가 되있고, 나머지 페이지에서는 로그인한 사람들만(쿠키를 가진사람들) 들어올 수 있게 선정하고,

로그아웃 표시로 치환

제품목록 페이지에서는 연결된 Database에서 리스트를 가져와 해당사에서 가지고 있는 제품들을 나열할 계획입니다.

그리고 신규로 들어오는 신제품들을 추가 할 수 있도록 우측 하단에 버튼식으로 <제품추가>를 만들고

링크를 연결하여 해당페이지로 연결할 계획입니다.(욕심으로는 페이지 이동이 아닌 모달을 사용하고 싶습니다.)

제품추가 페이지에서는

여러 항목들을 각기 맞는 필드들로 작성할 예정이고, 필수값들을 선정하여 필수값이 아닌 인풋에는 작성하지 않아도 추가할 수 있도록 만들 예정입니다.

그리고 우측 하단 제품추가 버튼을 클릭시에 제품이 추가되고 화면을 제품목록페이지로 자동으로 이동시켜 줄 계획입니다.

(모달 사용시 모달만 꺼지도록 구성)

입고 및 조정 페이지에서는 제품리스트를 나열하고 그 상단에는 검색기능(어떤 필드로도 검색가능 할 수 있게)을 추가할 예정입니다.

그리고, 제품명을 클릭시에 해당 제품상세페이지로 이동할 수 있게 만들 예정입니다.

제품상세 페이지에서는 사진과 같이 만들어갈 예정이고,

우측 하단에 수정하기 버튼을 만들어서 입력값들을 수정할 수 있게 만들 예정입니다. (수정하기 버튼 클릭시 수정완료 후 해당페이지로 이동)

다른페이지로 이동하지는 않고, 해당 페이지에서 수정가능 할 수 있게 할 계획인데, 욕심으로는 모달을 사용하고 싶습니다.

출고페이지에서는 제품리스트들을 나열하고

각 제품 좌측에 체크박스를 만들어서 삭제할 수 있게 할 계획입니다.

삭제 버튼 클릭 시 삭제한 제품은 리스트에서 삭제되고 화면은 출고페이지로 다시 이동.

히스토리 페이지에서는 입/출고를 선택할 수 있고, 제품명을 선택가능, 기간 선택 가능 (3가지 선택가능,미선택 가능)

이러한 기능들을 추가해서 선택한 내용에 맞게 입/출고한 내역들을 보여주는 페이지를 만들고 싶습니다.

히스토리페이지 및 대시보드(사진에 없음)는 생각보다 어려울 것 같아서 마지막에 할 계획입니다.

현재 파이썬 장고 프레임워크를 이용해서

1.로그인

2.회원가입

3.리스트보여주는 페이지

4.검색기능

5.페이징처리

6.네비게이션 바

등은 계속해서 연습중입니다. 웹개발종합반-플러스를 들으면서 따로 만든 개인 프로젝트를 아마존에 연결해 보았고,

실제 연구소 엑셀파일을 csv로 데이터베이스에 연동도 해보았습니다.

위에 기능들도 만들면서, 부트스트랩으로 화면도 더 이쁘게 만들어 보고 싶습니다.

반응형

파이썬 Python #23 파이썬리스트와 딕셔너리 2- 운석 피하기 게임, 딕셔너리

반응형

응용문제

(1)운석 피하기 게임

랜덤한 위치에 소행성을 10개 생성하고

10 밀리초마다, 소행성은 랜덤한 방향으로 회전 후 이동시키고,

10 밀리초마다, 거북이는 계속 전진하며, 방향키로 회전 방향을 바꿀 수 있도록

프로그램을 작성해봅시다

거북이의 색깔은 파란색, 소행성의 색깔은 빨간색으로 두세요

방향키 한 번을 눌렀을 때 회전방향은 30도로 두세요

1. 딕셔너리

딕셔너리(dictionary)도 리스트처럼 여러 값(value)들을 저장합니다

단, 숫자 형태의 인덱스 대신 다양한 키(key)를 사용 가능합니다

이렇게 값을 저장한 뒤 딕셔너리를 출력하면 여러 개의 키(홍길동, 김철수, 이영희)와 값(전화번호)들이 쭉 나옵니다

2. 딕셔너리에서 탐색

키를 가지고 값을 찾을 수 있습니다

3. 딕셔너리에서 키와 값을 가지고 출력해봅시다

print(phone_book[“강감찬”])

010-1234-5679

예제

한 학생에 대한 정보를 딕셔너리로 저장해봅시다

모든 항목들을 출력해봅시다

응용문제

(2) 편의점 재고 관리

편의점에서 재고 관리를 수행하는 프로그램을 작성해봅시다

판매하는 물건의 재고를 딕셔너리에 저장하고, 물건을 입력받아 수량을 출력해봅시다

그리고 재고 전체의 키와 값을 출력하고 각 품목과 재고를 한 줄씩로 출력해봅시다

(3) 영한사전

아주 간단한 영한사전을 만들어봅시다

영어 단어를 키로 하고 뜻을 값으로 하여 저장해봅시다

응용문제

(1)

(2)

(3)

반응형

[python] 도서 관리 프로그램

반응형

시나리오

도서 관리 프로그램은 콘솔 응용 프로그램이다. 응용에서는 사용자와 상호작용을 담당한다. 응용은 사용자에게 메뉴를 보여주고 선택한 메뉴를 수행하는 것을 반복한다. 메뉴에는 장르 추가, 도서 추가,도서 삭제, 도서 검색, 전체 도서 보기, 프로그램 종료가 있다. 장르 추가를 선택하면 추가할 장르 명을 입력받아 추가한다. 도서 추가를 선택하면 장르를 선택하고 도서 정보를 입력받아 추가한다. 도서는 ISBN(주요키), 도서명, 저자, 출판사, 가격 정보를 사용자로부터 입력받는다. 도서 삭제 기능에서는 도서의 ISBN을 사용자로부터 입력받아 삭제한다. 도서 검색 기능에서는 도서의 ISBN을 사용자로부터 입력받아 검색한다. 전체 보기에서는 전체 도서 정보를 출력한다. 프로그램 종료할 때 데이터를 파일에 저장하고 프로그램 시작할 때 로딩한다.

Fun.py

import os def SelectMenu(): os.system(‘cls’) print(“도서 관리 프로그램 v0.1.0”) print(“1:장르 추가”) print(“2:도서 추가”) print(“3:도서 삭제”) print(“4:도서 검색”) print(“5:전체 보기”) print(“0:종료”) return input(“메뉴 선택 ☞: “) def ViewGenres(genres): print(“◎◎◎ 장르 목록:({0}개)”.format(len(genres))) for i in range(0,len(genres)): print(“{0}:{1}”.format(i+1,genres[i]),end=’ ‘) print() print() def ViewBook(book): print(“{0}번 {1}도서 정보”.format(book[0], book[1])) print(“▷ 장르:{0} 저자:{1} 출판사:{2} 가격:{3}”.format(book[2],book[3],book[4],book[5])) print(“=============================”) def ViewBooks(books): print(“☆☆☆ 도서 목록:({0}개)”.format(len(books))) for book in books: ViewBook(book) def AddGenre(genres): print(“=========장르 추가==================”) ViewGenres(genres) genre = input(“추가할 장르 명:”) genres.append(genre) def Find(books,isbn): for i in range(0,len(books)): book = books[i] if book[0] == isbn: return i return -1 def AddBook(genres,books): print(“=========도서 추가==================”) ViewGenres(genres) gn = int(input(“장르 번호”)) if gn<=0 or gn>len(genres): print(“잘못 선택하였습니다.”) return isbn = input(“ISBN:”) bi = Find(books,isbn) if bi != -1: print(“이미 존재하는 ISBN이어서 추가할 수 없습니다.”) return title = input(“도서명:”) author = input(“저자:”) publisher = input(“출판사”) price = input(“가격:”) book =[isbn,title,gn,author,publisher,price] books.append(book) def RemoveBook(books): print(“=========도서 삭제==================”) isbn = input(“삭제할 도서의 ISBN:”) bi = Find(books,isbn) if bi == -1: print(“존재하지 않는 ISBN입니다.”) return book = books.pop(bi) ViewBook(book) print(“삭제하였습니다.”) def FindBook(books): print(“=========도서 검색==================”) isbn = input(“검색할 도서의 ISBN:”) bi = Find(books,isbn) if bi == -1: print(“존재하지 않는 ISBN입니다.”) return book = books[bi] ViewBook(book) def ViewAll(genres,books): print(“=========전체 보기==================”) ViewGenres(genres) ViewBooks(books) def Save(genres,books): print(“===============저장==================”) gfs = open(“genres.csv”,”w”) for genre in genres: gfs.write(genre+”

“) gfs.close() bfs = open(“books.csv”,’w’) for book in books: b_str=”” #한 권의 책 정보를 하나의 문자열로 표현하기 위한 변수 for elem in book: #책의 각 항목(elem)을 b_str += str(elem) +”,” #항목을 문자열로 변환 후 “,”를 더하여 b_str에 추가 b_str = b_str[:-1]+’

‘ #마자막 콤마를 제외한 문자열과 개행(‘

‘)을 합산 bfs.write(b_str) #ISBN,제목, 장르번호,저자,출판사,가격

” 문자열 저장 bfs.close() def Load(genres,books): print(“===============로딩==================”) try: gfs = open(“genres.csv”,”r”) datas = gfs.read() gfs.close() ds_gs = datas.split(‘

‘) for genre in ds_gs: if(genre==”): break genres.append(genre) bfs = open(“books.csv”,’r’) bs = bfs.read() bfs.close() bs_bs = bs.split(‘

‘) for bstr in bs_bs: bes = bstr.split(‘,’) if len(bes)<6: break isbn,title = bes[0], bes[1] gn = int(bes[2]) author, publisher,price = bes[3],bes[4],bes[5] book = [isbn,title,gn,author,publisher,price] books.append(book) except: print("처음 사용하시는 군요!!!") print("환영합니다.") return main.py from Fun import SelectMenu from Fun import AddBook from Fun import AddGenre from Fun import FindBook from Fun import RemoveBook from Fun import ViewAll from Fun import Save from Fun import Load def main(): genres =list() books = list() Load(genres,books) while True: key = SelectMenu() if key == '0': break elif key == '1': AddGenre(genres) elif key == '2': AddBook(genres,books) elif key == '3': RemoveBook(books) elif key == '4': FindBook(books) elif key == '5': ViewAll(genres,books) else: print("잘못 선택하였습니다.") print("엔터를 누르세요.") input() Save(genres,books) print("프로그램을 종료합니다.") #별도의 진입점 main함수에서 출발하시오. if __name__ == '__main__': main() 반응형

[데이터 관리 프로그램/Python] What’s in my Refrigerator : 냉장고 속 식재료 데이터 관리 프로그램

728×90

프로젝트 개요

데이터 관리 프로그램 팀 프로젝트

2020년도 2학기 / 2020.10.25-2020.12.04

자료구조/ Heap 구조 활용

파이썬 기반

코딩, 발표 , 발표 자료 제작

What’s in my Refrigerator는 냉장고 속 식재료 데이터를 관리하는 프로그램입니다. Heap 자료구조와 파이썬을 이용하여 구현하였습니다.

2020-2 데이터사이언스와 컴퓨팅2 (안용학 교수님)

2020년도 2학기에 안용학 교수님의 데이터사이언스와 컴퓨팅2 (알고리즘) 수업을 들으면서 한학기 동안 수행한 팀 프로젝트입니다.

한학기 동안 배운 자료구조를 하나 선정하여 데이터 관리 프로그램을 제작하는 프로젝트입니다.

What’s in my Refrigerator

What’s in my Refrigerator 는 냉장고 속 식재료 데이터를 관리하는 프로그램입니다.

Python을 기반으로 개발하였고 Heap 자료 구조를 사용했습니다.

What’s in my Refrigerator의 기능

냉장고에 있는 식재료 데이터를 유통기한 순으로 정렬하여 관리합니다.

식재료 데이터의 유통기한이 임박하면 알려줍니다.

냉장고 속 식재료를 이용하여 만들 수 있는 요리 레시피를 알려줍니다.

Heap을 자료구조로 사용한 이유

제자리 정렬이 가능하여 메모리 측면에서 유리합니다.

처음 프로그램 시작 시, 데이터 파일의 텍스트 등 많은 자료를 불러올 때 상향식 힙 생성으로 O(n)시간만을 소요하게 됩니다.

아이템을 추가할 때 O(log n)시간 만이 소요됩니다.

정렬 시, 힙 정렬 2기만 수행하면 돼서 간단합니다.

아이템 업데이트 업데이트한 자료를 기준으로 upheap 또는 downheap을 수행하여 정렬합니다.

아이템 제거 아이템 제거시 힙 속성을 유지하기 위해 루트 노드를 제거할 때와 유사한 방식으로 수행합니다. 수정할 자료와 마지막 자료의 위치를 변경합니다 마지막에 위치한 자료를 삭제한 것으로 간주합니다. 수정할 자료강 있던 자리에서 수정된 값에 따라 upheap 또는 downheap을 수행합니다.

class 다이어그램

주요 기능

Create 새로운 식재료를 냉장고에 넣을 때, 식재료 명과 유통기한을 함께 입력해서 리스트에 삽입합니다. 데이터 삽입 후 유통기한을 기준으로 오름차순 정렬을 수행하여 정렬된 상태를 유지합니다.

Read 사용자가 식재료 명을 입력했을 때 해당 식재료의 유통기한을 알려줍니다. 유통기한이 가장 임박한 재료를 알려줍니다. 냉장고 속 식재료의 총 개수를 알려줍니다.

Update 식재료 명을 수정하고 수정된 정보를 보여줍니다. 식재료 유통기한을 수정하고 수정된 정보를 보여줍니다.

Delete 식재료를 삭제하고 삭제한 데이터를 알려줍니다.

Print 냉장고 속 모든 식재료의 이름과 유통기한을 보여줍니다.

추가 기능 냉장고 속 재료로 만들 수 있는 요리를 알려줍니다.

보다 자세한 사항은 링크에 첨부된 파일들(제안서, 발표 자료, 소스코드 등)을 참고해주시면 감사하겠습니다 🙂

class Refrigerator

source code

Data_structure.py

데이터 관리에 사용한 자료 구조인 힙을 구현한 파일입니다.

소스 코드 보기:

더보기 import copy class Heap: ####################### ##### Heap Basics ##### ####################### def __init__(self): self.data = [None] self.last = 0 def left_child(self, i): if 2 * i > self.last: # 왼쪽 자식이 없다면, return None # None return 2 * i def right_child(self, i): if 2 * i + 1 > self.last: # 오른쪽 자식이 없다면, return None return 2 * i + 1 def parent(self, i): if i == 1: # 부모가 없다면, (루트라면,) return None return int(i / 2) def swap_element(self, i, j): self.data[i], self.data[j] = self.data[j], self.data[i] return # def downHeap(self, i): # if not self.left_child(i): # return # greater = self.left_child(i) # 왼쪽 자식이 있다면, 우선 greater = left # # if self.right_child(i): # 오른쪽 자식이 있다면, # if self.data[self.right_child(i)] > self.data[greater]: # 오른쪽 자식과 왼쪽 자식의 key 비교, 오른쪽이 더 크다면, # greater = self.right_child(i) # greater = right # # if self.data[i] >= self.data[greater]: # downHeap의 대상과 자식 중 더 큰것을 비교, 대상이 더 크거나 같다면, # return # Heap 속성을 만족하므로 종료. # # self.swap_element(i, greater) # 자식이 더 크다면, 서로 위치를 바꾸고, # self.downHeap(greater) # 바꾼 위치에서 다시 downHeap 수행. def downHeap_by_date(self, i): if not self.left_child(i): return greater = self.left_child(i) # 왼쪽 자식이 있다면, 우선 greater = left if self.right_child(i): # 오른쪽 자식이 있다면, if self.data[self.right_child(i)].get_date() > self.data[greater].get_date(): # 오른쪽 자식과 왼쪽 자식의 key 비교, 오른쪽이 더 크다면, greater = self.right_child(i) # greater = right if self.data[i].get_date() >= self.data[greater].get_date(): # downHeap의 대상과 자식 중 더 큰것을 비교, 대상이 더 크거나 같다면, return # Heap 속성을 만족하므로 종료. self.swap_element(i, greater) # 자식이 더 크다면, 서로 위치를 바꾸고, self.downHeap_by_date(greater) # 바꾼 위치에서 다시 downHeap 수행. # def downHeap_by_name(self, i): # if not self.left_child(i): # return # greater = self.left_child(i) # 왼쪽 자식이 있다면, 우선 greater = left # # if self.right_child(i): # 오른쪽 자식이 있다면, # if self.data[self.right_child(i)].get_name > self.data[greater].get_name: # 오른쪽 자식과 왼쪽 자식의 key 비교, 오른쪽이 더 크다면, # greater = self.right_child(i) # greater = right # # if self.data[i].get_name >= self.data[greater].get_name: # downHeap의 대상과 자식 중 더 큰것을 비교, 대상이 더 크거나 같다면, # return # Heap 속성을 만족하므로 종료. # # self.swap_element(i, greater) # 자식이 더 크다면, 서로 위치를 바꾸고, # self.downHeap_by_name(greater) # 바꾼 위치에서 다시 downHeap 수행. # def upHeap(self, i): # if not self.parent(i): # i가 루트라면, # return # # if self.data[i] <= self.data[self.parent(i)]: # Heap 속성을 만족한다면, # return # # self.swap_element(i, self.parent(i)) # i가 root가 아니고 Heap 속성도 만족하지 않는다면, swap # self.upHeap(self.parent(i)) # parent에서 upHeap 수행. def upHeap_by_date(self, i): if not self.parent(i): # i가 루트라면, return if self.data[i].get_date() <= self.data[self.parent(i)].get_date(): # Heap 속성을 만족한다면, return self.swap_element(i, self.parent(i)) # i가 root가 아니고 Heap 속성도 만족하지 않는다면, swap self.upHeap_by_date(self.parent(i)) # parent에서 upHeap 수행. # def upHeap_by_name(self, i): # if not self.parent(i): # i가 루트라면, # return # # if self.data[i].get_name() <= self.data[self.parent(i)].get_name(): # Heap 속성을 만족한다면, # return # # self.swap_element(i, self.parent(i)) # i가 root가 아니고 Heap 속성도 만족하지 않는다면, swap # self.upHeap_by_name(self.parent(i)) # parent에서 upHeap 수행. ################ ##### CRUD ##### ################ # def insert_item(self, item): # self.last += 1 # self.data.append(item) # self.upHeap(self.last) def insert_item_by_date(self, item): self.last += 1 self.data.append(item) self.upHeap_by_date(self.last) # def remove_max(self): # if self.last == 0: # return None # # self.swap_element(1, self.last) # root와 last swap # max = self.data.pop() # last 제거, max에 값 저장. # self.last -= 1 # # self.downHeap(1) # Heap 속성 복구 # return max def remove_max_by_date(self): if self.last == 0: return None self.swap_element(1, self.last) # root와 last swap max = self.data.pop() # last 제거, max에 값 저장. self.last -= 1 self.downHeap_by_date(1) # Heap 속성 복구 return max # def remove_idx(self, i): # if i > self.last: # return None # index 범위 초과 # # self.swap_element(i, self.last) # i와 last swap # removed = self.data.pop() # last 제거, removed에 값 저장. # self.last -= 1 # # self.upHeap(i) # Heap 속성 복구 # self.downHeap(i) # Heap 속성 복구 # return removed def remove_idx_by_date(self, i): if i > self.last: return None # index 범위 초과 self.swap_element(i, self.last) # i와 last swap removed = self.data.pop() # last 제거, removed에 값 저장. self.last -= 1 self.upHeap_by_date(i) # Heap 속성 복구 self.downHeap_by_date(i) # Heap 속성 복구 return removed ###################### ##### Heap inout ##### ###################### # self.data 정렬된 상태로 바꿈. # def in_place_heap_sort(self): # for i in range(self.last, 1, -1): # self.last 부터 2까지 # self.swap_element(1, i) # self.last -= 1 # self.downHeap_by_date(1) # return # 정렬된 새로운 list return # def get_sorted(self): # result = copy.deepcopy(self) # for i in range(result.last, 1, -1): # self.last 부터 2까지 # result.swap_element(1, i) # result.last -= 1 # result.downHeap(1) # return result def get_sorted_by_date(self): result = copy.deepcopy(self) for i in range(result.last, 1, -1): # self.last 부터 2까지 result.swap_element(1, i) result.last -= 1 result.downHeap_by_date(1) return result # def get_sorted_by_name(self): # result = copy.deepcopy(self) # for i in range(result.last, 1, -1): # self.last 부터 2까지 # result.swap_element(1, i) # result.last -= 1 # result.downHeap_by_name(1) # return result # def print_heap(self): # print(self.data) # def print_heap_items(self): # for item in self.data[1:]: # print(item) # def build_heap(self, data_list): # 비재귀적 상향식 힙생성. # self.data.extend(data_list) # self.last = len(data_list) # for i in range(self.last, 0, -1): # self.last 부터 1까지 # self.downHeap(i) # return def build_heap_by_date(self, data_list): # 비재귀적 상향식 힙생성. self.data.extend(data_list) self.last = len(data_list) for i in range(self.last, 0, -1): # self.last 부터 1까지 self.downHeap_by_date(i) return

Refrigerator.py

데이터 관리 프로그램의 CRUD를 처리하는 기능과 냉장고 속 식재료 데이터를 저장하는 텍스트 파일에서 데이터를 읽어오고, 변경되거나 추가된 데이터를 써서 저장하는 기능을 가지고 있는 가장 핵심이 되는 클래스입니다.

파일을 읽고 쓰기위한 메소드는 File_manage.py 파일에 따로 만들어 두고, 파일에서 필요한 정보를 읽고, 쓸때 임포트해서 사용했습니다.

데이터 파일에 각 아이템의 포맷은 “type, item_name, date, stock” 입니다. ex) ingredient,당근,2021-01-03,3

def read_file(cls, file_name): # file_name : 읽어올 파일의 이름. (Refrigerator.txt) with open(file_name, ‘rt’) as file: data = file.readlines() return data

File_manager에 정의된 read_file 메소드로 데이터 파일을 open해서 데이터를 readlines()로 읽어온 데이터를 반환받습니다.

for type,name,use_by_date,stock in (item.strip().split(‘,’) for item in FM.File_manager.read_file(“Refrigerator.txt”)): …

반환받은 각 데이터 라인들이 하나의 item이라고 생각하시면 됩니다. 그리고 ‘,’을 기준으로 split을 하여 type, name, use_by_date,stock에 저장합니다. 그리고 각 아이템들의 type에 따라 ingredient면 Ingredient 타입 배열에, dish면 Dish 타입 배열에 name, use_by_date, stock 값을 append해 줍니다.

contents = list() for item in self.data[1:]: item_str = “,”.join([item.type, item.name, item.use_by_date, str(item.stock)]) contents.append(item_str)

수정 또는 추가되는 아이템들의 name, use_by_date, stock 값을 각각 받아 data 배열에 저장해 두고, 그 배열의 각 아이템들을 type, name, use_sy_date, stock 형식에 맞게 변환한 후, content 라는 배열에 append합니다.

def write_file(cls, file_name, contents): with open(file_name, ‘wt’) as file: for item in contents: file.write(item+”

“) return

contents는 File_manager에 구현된 write_file 메소드로 전달됩니다. 기존 데이터 파일을 open하고, contents 배열에 저장된 아이템들을 write해줍니다.

File_manager.py 소스 코드 보기:

더보기 class File_manager: @classmethod def read_file(cls, file_name): # file_name : 읽어올 파일의 이름. (Refrigerator.txt) with open(file_name, ‘rt’) as file: data = file.readlines() return data # file_name : 쓸 파일의 이름. (Refrigerator.txt) / file_contents : 파일에 쓸 내용이 담긴 list @classmethod def write_file(cls, file_name, contents): with open(file_name, ‘wt’) as file: for item in contents: file.write(item+”

“) return # if __name__ == “__main__”: contents = [“test_data”] File_manager.write_file(“test_data.txt”, contents)

다음 주요 기능 CRUD 코드에 대한 설명입니다.

def ingredient_create(self, name, use_by_date, stock): # 새로운 Ingredient 추가 self.insert_item_by_date(Food.Ingredient(name, use_by_date, stock))

새로운 식재료 정보를 인자로 받아 추가하는 메소드입니다. 전달받은 인자들로 Food 중 ingredient 타입의 오브젝트를 만들어 아이템을 유효기간 날짜 순으로 정렬하면서 삽입하는 메소드로 전달합니다.

# Class Heap: … def insert_item_by_date(self, item): self.last += 1 self.data.append(item) self.upHeap_by_date(self.last) …

새로운 식재료 객체를 전달받은 힙 자료구조는 전체 아이템 개수를 나타내는 last 변수를 1 증가시키고, 아이템 객체들의 배열인 data에 새로운 객체를 append하고, 정렬을 위해 Heap의 upHeap_by_date 메소드을 호출합니다.

# Class Heap: def upHeap_by_date(self, i): if not self.parent(i): # i가 루트라면, return if self.data[i].get_date() <= self.data[self.parent(i)].get_date(): # Heap 속성을 만족한다면, return self.swap_element(i, self.parent(i)) # i가 root가 아니고 Heap 속성도 만족하지 않는다면, swap self.upHeap_by_date(self.parent(i)) # parent에서 upHeap 수행. 아이템 전체 개수를 i 인자로 받은 upHeap_by_date는 현재 힙 자료구조가 빈 상태였다면 새로 추가된 객체가 루트일 수 있습니다. 이 경우 정렬할 필요가 없으므로 그냥 return 해줍니다. 그리고 유효기간의 날짜에 따라 재귀적으로 정렬을 수행하며 힙 속성을 복구해 줍니다. def ingredient_read_all(self): cnt = 1 for item in self.data[1:]: print(f"{str(cnt),{item}") cnt += 1 저장되어 있는 식재료 데이터 전체를 보여주는 메소드입니다. data 배열 처음부터 끝까지 프린트해줍니다. cnt는 보여줄 때 인덱스로 사용하기 위해 사용했습니다. def ingredient_read_all_by_date(self): for item in self.get_sorted_by_date().data[:]: print(item) ingredient_read_all 메소드가 단순히 data 배열에 저장된 순서대로 아이템을 나열하는 메소드라면 이 메소드는 use_by_date를 기준으로 정렬된 순서로 item을 보여주는 메소드입니다. 위와 마찬가지로 data의 처음부터 끝까지 출력하는데 이때 get_sorted_by_date()를 먼저 호출해줍니다. # Class Heap: ... def get_sorted_by_date(self): result = copy.deepcopy(self) for i in range(result.last, 1, -1): # self.last 부터 2까지 result.swap_element(1, i) result.last -= 1 result.downHeap_by_date(1) return result ... ** copy.deepcopy는 파이썬 내장 모듈인 copy을 임포트해서 사용하는 메소드로 원본 배열의 주소 값을 복사해서 참조하는 것이 아니라 원본 배열 객체 자체를 복사하는 것입니다. 이를 통해 Heap 클래스 객체 자체를 복사해서 result에 저장하고, 복사된 객체에 저장된 모든 노드를 for loop을 돌게하여 downHeap을 수행시킵니다. # class Heap: ... def downHeap_by_date(self, i): if not self.left_child(i): return greater = self.left_child(i) # 왼쪽 자식이 있다면, 우선 greater = left if self.right_child(i): # 오른쪽 자식이 있다면, if self.data[self.right_child(i)].get_date() > self.data[greater].get_date(): # 오른쪽 자식과 왼쪽 자식의 key 비교, 오른쪽이 더 크다면, greater = self.right_child(i) # greater = right if self.data[i].get_date() >= self.data[greater].get_date(): # downHeap의 대상과 자식 중 더 큰것을 비교, 대상이 더 크거나 같다면, return # Heap 속성을 만족하므로 종료. self.swap_element(i, greater) # 자식이 더 크다면, 서로 위치를 바꾸고, self.downHeap_by_date(greater) # 바꾼 위치에서 다시 downHeap 수행.

downHeap을 수행할 현재 노드를 인자로 전달받습니다. 이 값에 왼쪽 자식 노드가 없다면 이 노드가 루트노드임을 의미하기 때문에 재귀를 끝냅니다. 오른쪽 자식 노드가 있다면 use_by_date을 비교해서 왼쪽 자식노드와 오른쪽 자식노드 중 그 값이 큰 값을 greater에 저장하고, 현재 노드와 greater 중 현재 노드가 더 크거나 같다면 힙 속성을 만족하는 것이므로 downHeap을 끝냅니다. 또는 자식이 더 큰 경우에는 그 값이 상위로 올라가야 하기 때문에 현재노드와 greater 노드의 위치를 스왑하고 바꾼 위치를 다시 재귀 함수 인자로 전달하여 downHeap을 수행합니다. 즉, 힙 속성을 만족하면 멈출 수 있는 재귀함수 입니다.

def ingredient_update_name(self, idx, new_name): self.data[idx].update_name(new_name) # Class Food: .. def update_name(self, new_name): self.name = new_name

데이터의 인덱스 값으로 접근하여 해당 아이템에 새로운 이름을 지정하는 메소드입니다. Food 클래스의 setter 메소드인 update_name을 사용합니다.

def ingredient_update_use_by_date(self, idx, new_use_by_date): old_use_by_date = self.data[idx].use_by_date self.data[idx].update_use_by_date(new_use_by_date) # key 가 달라지므로, heap 속성 복구를 위해 수행 if old_use_by_date < new_use_by_date: self.upHeap_by_date(idx) else: self.downHeap_by_date(idx) 데이터의 인덱스로 접근해 use_by_date 값을 수정하는 메소드입니다. 이 값이 정렬 key이기 때문에 이 값을 수정하면 힙 속성 복구를 위해 upHeap 과 downHeap을 수행해야 합니다. def ingredient_update_stock(self, idx, new_stock): self.data[idx].update_stock(new_stock) # Class Food :... def update_stock(self, new_stock): self.stock = new_stock 재고수를 업데이트하는 메소드로 이 역시 Food 클래스 메소드를 사용합니다. def ingredient_delete_idx(self, idx): return self.remove_idx_by_date(idx) # return 값 : 삭제된 값 / 인덱스 범위 초과시 None 주어진 인덱스 값의 데이터를 제거하는 메소드입니다. 값을 제거한 후 힙 속성 복구를 위한 작업이 필요하기 때문에 힙 클래스의 메소드를 호출합니다. # Class Heap:... def remove_idx_by_date(self, i): if i > self.last: return None # index 범위 초과 self.swap_element(i, self.last) # i와 last swap removed = self.data.pop() # last 제거, removed에 값 저장. self.last -= 1 self.upHeap_by_date(i) # Heap 속성 복구 self.downHeap_by_date(i) # Heap 속성 복구 return removed … def swap_element(self, i, j): self.data[i], self.data[j] = self.data[j], self.data[i] return

삭제하려고 하는 데이터의 인덱스가 현재 데이터에 저장된 아이디 보다 큰 경우, None을 반환합니다. 일단 제거하려는 아이템 인덱스와 객체 배열의 가장 마지막에 저장된 아이템 인덱스를 swap_elemet에 전달해 스왑합니다. 그러면 last에 제거하려는 값이 저장되기 때문에 이 값을 배열에서 pop하고 그 값은 반환하기 위해 removed 변수에 따로 저장해둡니다. 그리고 배열 전체 개수를 나타내는 변수 last를 1 감소 시킵니다. 그런 뒤, 힙 속성을 복구시키기 위해 추가된 아이템 노드를 기준으로 upHeap과 downHeap을 수행하고, removed 값을 반환합니다.

다음은 추가 기능에 대한 설명입니다.

def ingredient_passed_date(self): result = [item for item in self.data[1:] if item.use_by_date < str(datetime.date.today())] if result: return result return None 저장된 아이템 중 유통기한이 지난 아이템을 보여주는 메소드입니다. 모든 아이템들의 use_by_date 를 오늘 날짜와 비교하여 지난 아이템들을 result라는 변수에 저장해서 보여줍니다. def ingredient_near_date(self): result = [item for item in self.data[1:] if 0 <= date_comparison(item.use_by_date, str(datetime.date.today())) < 3] if result: return result return None ... def date_comparison(date1, date2): date1_value = (int(date1[:4])-2020) * 365 + (int(date1[5:7]) * 31) + (int(date1[8:10])) date2_value = (int(date2[:4])-2020) * 365 + (int(date2[5:7]) * 31) + (int(date2[8:10])) return date1_value - date2_value 유통기한이 임박한 식재료를 알려주기 위한 메소드입니다. ingredient_passed_date 메소드와 유사하나 그 값을 오늘 날짜와 비교하여 0 보다 크거나 같은 값을 저장해 반환합니다. 다음은 가지고 있는 식재료를 이용하여 만들 수 있는 요리를 알려주는 기능을 위한 메소드에 대한 소개입니다. def make_dish(self, recipe): if not self.check_condition(recipe): return False for idx in range(1,len(self.data) - 1): item = self.data[idx] if item.name in recipe.ingredients: if not item.reduce_stock(): self.remove_idx_by_date(idx) if recipe.name in self.kinds_of_items(): for item in self.data[1:]: if item.name == recipe.name: item.stock += 1 else: self.ingredient_create(recipe.name, str(datetime.date.today()), 1) return True ... def reduce_stock(self, idx): if not self.data[idx].reduce_stock(): self.remove_idx_by_date(idx) return False return True def kinds_of_items(self): kinds = set() for item in self.data[1:]: kinds.add(item.name) return kinds def check_condition(self, recipe): for item in recipe.ingredients: if item not in self.kinds_of_items(): return False return True Recipe_book 클래스 객체를 이용합니다. Recipe_book 객체는 recipe 객체 배열을 가지고 있습니다. 이 배열은 recipe 객체는 "요리명/식재료1, 식재료,2.." 와 같은 포맷입니다. ex) 치즈감자전/감자,치즈 Recipe_book에 저장된 recipe 객체 배열 중 하나를 인자로 전달 받은 make_dish 메소드는 이 recipe에 필요한 레시피 중 하나라도 냉장고에 있는 식재료가 아닌 경우 만들 수 없으므로 false를 반환합니다. 이렇게 check_condition을 통과한 뒤, 냉장고에 저장된 모든 아이템에 중 recipe에 사용될 식재료는 사용했으므로 제고 수를 줄이고, 재고수가 더 이상 없는 경우도 만들 수 없으므로 false를 반환합니다. Refrigerator.py소스 코드 보기 : 더보기 import Data_structure import File_manager as FM import Food import datetime def date_comparison(date1, date2): date1_value = (int(date1[:4])-2020) * 365 + (int(date1[5:7]) * 31) + (int(date1[8:10])) date2_value = (int(date2[:4])-2020) * 365 + (int(date2[5:7]) * 31) + (int(date2[8:10])) return date1_value - date2_value class Refrigerator(Data_structure.Heap): ############################# ##### File read / write ##### ############################# def __init__(self): super().__init__() items = list() for type,name,use_by_date,stock in (item.strip().split(',') for item in FM.File_manager.read_file("Refrigerator.txt")): if type == "ingredient": items.append(Food.Ingredient(name, use_by_date, stock)) elif type == "dish": items.append(Food.Dish(name, use_by_date, stock)) else: pass self.build_heap_by_date(items) def save_refrigerator(self): contents = list() for item in self.data[1:]: item_str = ",".join([item.type, item.name, item.use_by_date, str(item.stock)]) contents.append(item_str) FM.File_manager.write_file("Refrigerator.txt", contents) ################ ##### CRUD ##### ################ def ingredient_create(self, name, use_by_date, stock): # 새로운 Ingredient 추가 self.insert_item_by_date(Food.Ingredient(name, use_by_date, stock)) def ingredient_read_all(self): cnt = 1 for item in self.data[1:]: print(f"{str(cnt)}. {item}") cnt += 1 def ingredient_read_all_by_date(self): for item in self.get_sorted_by_date().data[1:]: print(item) def ingredient_update_name(self, idx, new_name): self.data[idx].update_name(new_name) def ingredient_update_use_by_date(self, idx, new_use_by_date): old_use_by_date = self.data[idx].use_by_date self.data[idx].update_use_by_date(new_use_by_date) # key 가 달라지므로, heap 속성 복구를 위해 수행 if old_use_by_date < new_use_by_date: self.upHeap_by_date(idx) else: self.downHeap_by_date(idx) def ingredient_update_stock(self, idx, new_stock): self.data[idx].update_stock(new_stock) def ingredient_delete_idx(self, idx): return self.remove_idx_by_date(idx) # return 값 : 삭제된 값 / 인덱스 범위 초과시 None ######################################### ########## Additional Features ########## ######################################### def ingredient_passed_date(self): result = [item for item in self.data[1:] if item.use_by_date < str(datetime.date.today())] if result: return result return None def ingredient_near_date(self): result = [item for item in self.data[1:] if 0 <= date_comparison(item.use_by_date, str(datetime.date.today())) < 3] if result: return result return None def check_use_by_date(self): passed = self.ingredient_passed_date() near = self.ingredient_near_date() return passed, near ##### Recipe ##### def reduce_stock(self, idx): if not self.data[idx].reduce_stock(): self.remove_idx_by_date(idx) return False return True def kinds_of_items(self): kinds = set() for item in self.data[1:]: kinds.add(item.name) return kinds def check_condition(self, recipe): for item in recipe.ingredients: if item not in self.kinds_of_items(): return False return True def make_dish(self, recipe): if not self.check_condition(recipe): return False for idx in range(1,len(self.data) - 1): item = self.data[idx] if item.name in recipe.ingredients: if not item.reduce_stock(): self.remove_idx_by_date(idx) if recipe.name in self.kinds_of_items(): for item in self.data[1:]: if item.name == recipe.name: item.stock += 1 else: self.ingredient_create(recipe.name, str(datetime.date.today()), 1) return True if __name__ == "__main__": main_refrigerator = Refrigerator() main_recipe_book = Food.Recipe_book() main_refrigerator.make_dish(main_recipe_book.data[0]) Class Food class Food: def __init__(self, name, use_by_date, stock): self.name = name self.use_by_date = use_by_date self.stock = int(stock) def update_name(self, new_name): self.name = new_name def update_use_by_date(self, new_use_by_date): self.use_by_date = new_use_by_date def update_stock(self, new_stock): self.stock = new_stock def reduce_stock(self): self.stock -= 1 if self.stock == 0: return False # 재고가 다 떨어져 자동으로 삭제될 경우 False return True # 재고가 남았을 경우 True def get_date(self): return self.use_by_date # def get_name(self): # return self.name def __str__(self): # print(Ingredient) return f"{self.name:<6} : {self.use_by_date} ({self.stock:>2})” class Ingredient(Food): type = “ingredient” class Dish(Food): type = “dish”

Class Recipe_book

class Recipe_book: class recipe: def __init__(self, name, ingredients): self.name = name self.ingredients = ingredients # ingredients : 요리에 사용되는 식재료(Ingredient)를 포함하는 list def __str__(self): return f”{self.name:<10} : {', '.join(self.ingredients)}" def __init__(self): self.data = list() for line in FM.File_manager.read_file("Recipe.txt"): if len(line) < 3: continue name, ingredients = line.strip().split('/') ingredients = ingredients.split(',') self.data.append(self.recipe(name, ingredients)) def create_recipe(self, name, ingredients): self.data.append(self.recipe(name, ingredients)) def save_recipe(self): contents = list() for item in self.data: item_str = item.name + "/" + ",".join(item.ingredients) contents.append(item_str) FM.File_manager.write_file("Recipe.txt", contents) def print_all_recipe(self): for i in range(len(self.data)): print(f"{i+1}. {self.data[i]}") 실행을 위한 메인 메소드 입니다. import Refrigerator import Food # User Interface opening_str = """[Refrigerator CRUD]""" choice_str = """\ ******************* [1] CREATE [2] READ [3] UPDATE [4] DELETE [5] Additional Features [6] Exit ******************* Enter the number : """ additional_str = """\ ******************* [1] Check Use by Date [2] Food Consumption [3] Cookable Dish [4] Cook Dish [5] Check Recipe [6] New Recipe [7] Return to main menu ******************* Enter the number : """ division_str = " ******************** " update_str = """\ ******************* [1] UPDATE name [2] UPDATE use by date [3] UPDATE stock ******************* Enter the number : """ return_msg = "Returning to the main menu" error_msg = "error : " + return_msg print(f" {opening_str} ") main_refrigerator = Refrigerator.Refrigerator() main_recipe_book = Food.Recipe_book() while True: try: choice = (input(choice_str)) choice = int(choice) print(division_str) print() if choice == 1: # create - insert ingredient to the refrigerator name = input("name : ") use_by_date = input("use by date (YYYY-MM-DD) : ") stock = input("number of stock : ") main_refrigerator.ingredient_create(name, use_by_date, stock) elif choice == 2: # show all items in refrigerator main_refrigerator.ingredient_read_all_by_date() elif choice == 3: # update information of ingerdiens main_refrigerator.ingredient_read_all() idx = int(input("Enter the number to modify : ")) update_op = int(input(update_str)) if update_op == 1: # name update main_refrigerator.ingredient_update_name(idx, input(f"new name of ({main_refrigerator.data[idx]}): ")) elif update_op == 2: # use by date update main_refrigerator.ingredient_update_use_by_date(idx, input(f"new use by date of ({main_refrigerator.data[idx]})(YYYY-MM-DD) : ")) elif update_op == 3: # stock update main_refrigerator.ingredient_update_stock(idx, int(input(f"new stock of ({main_refrigerator.data[idx]}) : "))) else: print(error_msg) elif choice == 4: # delete a specific ingredient main_refrigerator.ingredient_read_all() idx = int(input("Enter the number to delete : ")) removed = main_refrigerator.ingredient_delete_idx(idx) if removed: print(f"{removed} deleted") else: print(error_msg) elif choice == 5: # Adittional choice = int(input(additional_str)) print() if choice == 1: # check use by date passed, near = main_refrigerator.check_use_by_date() if passed: print("***** Expired *****") for item in passed: print(item) if near: print("***** near date *****") for item in near: print(item) if (not passed) & (not near): print("all items has enough time.") elif choice == 2: # Food consumption print() main_refrigerator.ingredient_read_all() idx = int(input("Enter the number to consume: ")) consumption = int(input("consumption : ")) temp_name = main_refrigerator.data[idx].name if consumption > main_refrigerator.data[idx].stock: print(“not enough stock”) print(return_msg) for i in range(consumption): if not main_refrigerator.reduce_stock(idx): print(f”all {temp_name} consumed”) input() elif choice == 3: # Cookable Dish flag = False for recipe in main_recipe_book.data: if main_refrigerator.check_condition(recipe): print(recipe) flag = True if not flag: print(“nothing available”) elif choice == 4: # Cook Dish print(main_recipe_book.print_all_recipe()) if not main_refrigerator.make_dish(main_recipe_book.data[int(input(“choose a number : “)) – 1]): print(“insufficient ingredients”) elif choice == 5: # Check Recipe main_recipe_book.print_all_recipe() elif choice == 6: # New Recipe name = input(“Dish name : “) ingredients = list() ingredients_cnt = int(input(“number of ingredients (int) : “)) for i in range(ingredients_cnt): ingredients.append(input(f”ingredient{i+1} : “)) main_recipe_book.create_recipe(name, ingredients) elif choice == 7: # Return to Main Menu print(return_msg) else: print(error_msg) elif choice == 6: main_refrigerator.save_refrigerator() main_recipe_book.save_recipe() print(“file saved”) print(“exit program”) break else: print(error_msg) input() # 입력 대기 : 바로 메인화면으로 넘어가는것을 방지. except: print(error_msg) if choice == “exit”: break

프로젝트 리뷰

구현을 위해 힙 자료 구조에 대해 정확하게 공부할 수 있는 좋은 기회였습니다. 또한 프로젝트 구현 전, 제안서를 작성하면서 관련 다이어그램을 그려볼 수 있었습니다. 또한 파이썬 코딩 실력이 뛰어나신 팀원 분과 협업을 통해 개발을 하면서 파이썬 코드에 대해서도 많이 배우고, 새로운 모듈 등에 대해서도 공부할 수 있었습니다.

소스 코드 및 관련 문서

DSC2_팀프로젝트_박준석, 김승윤.zip 0.98MB

728×90

Hell..o World..!! (너래쟁이) :: PYTHON 9장 리스트와 딕셔너리

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 arr = [] for num in range ( 1 , 21 ): arr.append(num) print ( “리스트 : ” ,arr) for a in arr: if (a % 2 = = 0 ): arr.remove(a) for a in arr: if (a % 3 = = 0 ): arr.remove(a) for a in arr: if (a % 4 = = 0 ): arr.remove(a) print ( “리스트 : ” ,arr) cs

키워드에 대한 정보 파이썬 재고 관리 프로그램

다음은 Bing에서 파이썬 재고 관리 프로그램 주제에 대한 검색 결과입니다. 필요한 경우 더 읽을 수 있습니다.

이 기사는 인터넷의 다양한 출처에서 편집되었습니다. 이 기사가 유용했기를 바랍니다. 이 기사가 유용하다고 생각되면 공유하십시오. 매우 감사합니다!

사람들이 주제에 대해 자주 검색하는 키워드 혹시 재고관리를 어떻게 하고계신가요? 안하고 계시진 않으신가요? 재고관리 프로그램이 필요하시면 다운로드받으세요.

  • 재고관리
  • 솔루션
  • 매크로
  • 액셀매크로
  • 엑셀
  • 무료
  • 나눔
  • 공개

혹시 #재고관리를 #어떻게 #하고계신가요? #안하고 #계시진 #않으신가요? #재고관리 #프로그램이 #필요하시면 #다운로드받으세요.


YouTube에서 파이썬 재고 관리 프로그램 주제의 다른 동영상 보기

주제에 대한 기사를 시청해 주셔서 감사합니다 혹시 재고관리를 어떻게 하고계신가요? 안하고 계시진 않으신가요? 재고관리 프로그램이 필요하시면 다운로드받으세요. | 파이썬 재고 관리 프로그램, 이 기사가 유용하다고 생각되면 공유하십시오, 매우 감사합니다.

See also  소방 안전 관리자 자격증 | 3대 노후 준비 중 소방 자격증 중에 소방안전관리자 1급 자격증 취업은 어떨까? 답을 믿으세요

Leave a Comment