<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>The life of TB</title>
    <link>https://techbard.tistory.com/</link>
    <description>Live for the present, Dream of the future, Learn from the past.</description>
    <language>ko</language>
    <pubDate>Mon, 13 Jul 2026 00:49:43 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>techbard</managingEditor>
    <image>
      <title>The life of TB</title>
      <url>https://t1.daumcdn.net/cfile/tistory/275D605052DF7B3223</url>
      <link>https://techbard.tistory.com</link>
    </image>
    <item>
      <title>OCI VM에 nginx 웹 서버 설치</title>
      <link>https://techbard.tistory.com/3221</link>
      <description>&lt;h1&gt;OCI AMD VM (1GB RAM / 1 CPU) Nginx 설치 가이드&lt;/h1&gt;
&lt;h2&gt;1. 사전 준비 — OCI 보안 그룹 설정&lt;/h2&gt;
&lt;p&gt;OCI 콘솔에서 인스턴스의 &lt;strong&gt;보안 목록(Security List)&lt;/strong&gt;에 다음 규칙을 추가하세요:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;방향&lt;/th&gt;
&lt;th&gt;프로토콜&lt;/th&gt;
&lt;th&gt;소스/대상&lt;/th&gt;
&lt;th&gt;포트&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Ingress&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;0.0.0.0/0&lt;/td&gt;
&lt;td&gt;80&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ingress&lt;/td&gt;
&lt;td&gt;TCP&lt;/td&gt;
&lt;td&gt;0.0.0.0/0&lt;/td&gt;
&lt;td&gt;443&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;OCI는 기본적으로 방화벽이 꽤 tight합니다. 이걸 안 하면 접속이 안 됩니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;hr&gt;
&lt;h2&gt;2. 시스템 업데이트&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Ubuntu/Debian 기준
sudo apt update &amp;amp;&amp;amp; sudo apt upgrade -y

# Oracle Linux/RHEL 기준
sudo dnf update -y&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;3. Nginx 설치&lt;/h2&gt;
&lt;h3&gt;Ubuntu/Debian&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt install nginx -y&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Oracle Linux&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo dnf install nginx -y&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;4. 서비스 시작 및 자동 시작 설정&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo systemctl start nginx
sudo systemctl enable nginx
sudo systemctl status nginx&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;정상이면 &lt;code&gt;active (running)&lt;/code&gt; 이라고 나옵니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;5. 방화벽 확인&lt;/h2&gt;
&lt;h3&gt;Ubuntu (ufw)&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo ufw allow &amp;#39;Nginx Full&amp;#39;
sudo ufw status&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Oracle Linux (firewalld)&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;6. 동작 확인&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl http://localhost&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;또는 브라우저에서 &lt;code&gt;http://&amp;lt;공인IP&amp;gt;&lt;/code&gt; 접속 → Nginx 환영 페이지가 뜨면 성공.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;7. 1GB RAM 최적화 (중요)&lt;/h2&gt;
&lt;p&gt;1GB 메모리는 빠듯하기 때문에 기본 설정을 조정해야 합니다:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo nano /etc/nginx/nginx.conf&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;worker_processes 1;          # CPU 1개이므로 1로 설정

events {
    worker_connections 256;  # 기본 1024 → 256으로 축소
}

http {
    # ... 기존 설정 유지 ...

    # 메모리 절약 설정
    client_body_buffer_size 8k;
    client_header_buffer_size 1k;
    large_client_header_buffers 2 8k;
    client_max_body_size 10m;

    # 버퍼 최적화
    output_buffers 1 32k;

    # Gzip 압축 (CPU vs 메모리 트레이드오프)
    gzip on;
    gzip_types text/plain text/css application/json application/javascript;
    gzip_min_length 256;
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 설정 문법 확인
sudo nginx -t

# 적용
sudo systemctl reload nginx&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;8. Swap 설정 (OOM 방지)&lt;/h2&gt;
&lt;p&gt;1GB RAM에서 메모리 부족 시 프로세스가 죽는 걸 방지:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo fallocate -l 1G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

# 영구 적용
echo &amp;#39;/swapfile none swap sw 0 0&amp;#39; | sudo tee -a /etc/fstab

# 확인
free -h&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;9. 기본 사이트 설정&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo nano /etc/nginx/sites-available/default&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-nginx&quot;&gt;server {
    listen 80;
    server_name _;

    root /var/www/html;
    index index.html;

    location / {
        try_files $uri $uri/ =404;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo nginx -t
sudo systemctl reload nginx&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;10. SSL 인증서 (Let&amp;#39;s Encrypt)&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo apt install certbot python3-certbot-nginx -y   # Ubuntu
sudo certbot --nginx -d your-domain.com&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;자동 갱신 확인:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;sudo systemctl status certbot.timer&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;빠른 점검 체크리스트&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 서비스 상태
systemctl is-active nginx

# 포트 리스닝 확인
ss -tlnp | grep nginx

# 메모리 사용량
ps aux | grep nginx

# 로그 확인
sudo tail -f /var/log/nginx/access.log
sudo tail -f /var/log/nginx/error.log&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;&lt;strong&gt;참고:&lt;/strong&gt; OCI 무료 티어 AMD VM은 1GB RAM이라 트래픽이 많으면 금방 빡빡해집니다. 정적 사이트 위주로 운영하고, 동적 애플리케이션은 별도로 고려하세요.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;</description>
      <category>자작 프로그램</category>
      <author>techbard</author>
      <guid isPermaLink="true">https://techbard.tistory.com/3221</guid>
      <comments>https://techbard.tistory.com/3221#entry3221comment</comments>
      <pubDate>Wed, 22 Apr 2026 17:15:45 +0900</pubDate>
    </item>
    <item>
      <title>OCI VM 공용 IP 할당하기</title>
      <link>https://techbard.tistory.com/3220</link>
      <description>&lt;p&gt;웹 서버 구축을 위해 OCI로 VM 생성 중에 있다.&lt;/p&gt;
&lt;p&gt;공용 IP 할당한 것이 (임시)라고 나와서 다른 검색 결과 따라서 해보는데 다들 옛날 UI 기반으로 되어 있어서 안 된다.&lt;/p&gt;
&lt;p&gt;그러나, 제미나이가 답을 주었으니&lt;/p&gt;
&lt;p&gt;오라클 클라우드(OCI) 무료 티어에서 &lt;strong&gt;임시(Ephemeral) 공용 IP&lt;/strong&gt;를 &lt;strong&gt;예약된(Reserved) 공용 IP&lt;/strong&gt;로 전환하려면, 기존의 임시 IP를 바로 &amp;#39;고정&amp;#39; 상태로 바꾸는 것이 아니라 &lt;strong&gt;기존 임시 IP를 제거한 후 새 예약 IP를 할당&lt;/strong&gt;하는 과정을 거쳐야 합니다.&lt;/p&gt;
&lt;p&gt;다만, 최근 OCI 업데이트로 &lt;strong&gt;현재 사용 중인 임시 IP를 즉시 예약 IP로 승격(Reserve)&lt;/strong&gt;시키는 기능이 추가되었습니다. 가장 간편한 방법부터 단계별로 안내해 드립니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;방법 1: 현재 사용 중인 임시 IP를 즉시 예약하기 (추천)&lt;/h2&gt;
&lt;p&gt;가장 빠른 방법입니다. 현재 인스턴스에 할당된 IP 주소를 그대로 유지하면서 예약 상태로 변경할 수 있습니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;OCI 콘솔&lt;/strong&gt;에 로그인합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;네트워킹(Networking)&lt;/strong&gt; &amp;gt; &lt;strong&gt;가상 클라우드 네트워크(VCN)&lt;/strong&gt;로 이동합니다.&lt;/li&gt;
&lt;li&gt;해당 인스턴스가 속한 &lt;strong&gt;VCN&lt;/strong&gt;을 클릭한 후, 왼쪽 메뉴에서 &lt;strong&gt;서브넷(Subnets)&lt;/strong&gt;을 선택합니다.&lt;/li&gt;
&lt;li&gt;인스턴스가 연결된 &lt;strong&gt;공용 서브넷&lt;/strong&gt;을 클릭합니다.&lt;/li&gt;
&lt;li&gt;왼쪽 &lt;strong&gt;리소스(Resources)&lt;/strong&gt; 메뉴에서 &lt;strong&gt;IPv4 주소(IPv4 Addresses)&lt;/strong&gt;를 클릭합니다.&lt;/li&gt;
&lt;li&gt;목록에서 현재 인스턴스가 사용 중인 IP 주소 오른쪽의 &lt;strong&gt;작업(점 3개)&lt;/strong&gt; 메뉴를 누릅니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;공용 IP 주소 예약(Reserve IP address)&lt;/strong&gt;을 선택하고 이름을 입력한 뒤 확인을 누릅니다.&lt;ul&gt;
&lt;li&gt;이제 해당 IP는 인스턴스가 삭제되어도 사라지지 않는 &amp;#39;예약된 IP&amp;#39;가 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;방법 2: 새 예약 IP를 생성하여 할당하기 (전통적 방식)&lt;/h2&gt;
&lt;p&gt;만약 위 메뉴가 보이지 않거나 새로운 IP를 고정하고 싶을 때 사용합니다.&lt;/p&gt;
&lt;h3&gt;1단계: 임시 IP 제거 (No Public IP)&lt;/h3&gt;
&lt;p&gt;하나의 VNIC에는 하나의 공용 IP만 가질 수 있으므로 기존 것을 먼저 제거해야 합니다.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;컴퓨트(Compute)&lt;/strong&gt; &amp;gt; &lt;strong&gt;인스턴스(Instances)&lt;/strong&gt;에서 해당 인스턴스를 선택합니다.&lt;/li&gt;
&lt;li&gt;하단 &lt;strong&gt;리소스&lt;/strong&gt; 탭에서 &lt;strong&gt;연결된 VNIC(Attached VNICs)&lt;/strong&gt;를 클릭합니다.&lt;/li&gt;
&lt;li&gt;기본 VNIC 이름을 클릭한 후, 왼쪽 &lt;strong&gt;IP 주소(IP Addresses)&lt;/strong&gt;를 선택합니다.&lt;/li&gt;
&lt;li&gt;기본 전용 IP 오른쪽의 &lt;strong&gt;편집(Edit)&lt;/strong&gt;을 누릅니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;공용 IP 없음(No Public IP)&lt;/strong&gt;을 선택하고 &lt;strong&gt;업데이트&lt;/strong&gt;합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;2단계: 예약된 공용 IP 할당&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;다시 같은 위치에서 &lt;strong&gt;편집(Edit)&lt;/strong&gt;을 누릅니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;공용 IP 유형&lt;/strong&gt;에서 &lt;strong&gt;예약된 공용 IP(Reserved public IP)&lt;/strong&gt;를 선택합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;새 예약된 공용 IP 생성&lt;/strong&gt;을 선택하고 이름을 입력합니다. (이미 만들어둔 예약 IP가 있다면 &amp;#39;기존 예약된 IP 주소 선택&amp;#39; 사용)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;업데이트&lt;/strong&gt;를 누르면 고정된 IP가 할당됩니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;  주의사항&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;무료 티어 제한:&lt;/strong&gt; 오라클 무료 계정은 지역(Region)당 최대 &lt;strong&gt;2개의 예약된 공용 IP&lt;/strong&gt;만 무료로 제공합니다. 초과 시 비용이 발생할 수 있으니 주의하세요.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;인스턴스 설정:&lt;/strong&gt; IP가 변경된 경우, 만약 도메인 연결(DNS)이나 화이트리스트 설정을 해두셨다면 새 IP로 업데이트해야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;유휴 리소스:&lt;/strong&gt; 예약만 해두고 인스턴스에 할당하지 않은 IP는 유료로 전환될 가능성이 있으므로(정책에 따라 상이), 사용하지 않는 예약 IP는 반드시 삭제하시기 바랍니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>자작 프로그램</category>
      <author>techbard</author>
      <guid isPermaLink="true">https://techbard.tistory.com/3220</guid>
      <comments>https://techbard.tistory.com/3220#entry3220comment</comments>
      <pubDate>Wed, 22 Apr 2026 15:58:10 +0900</pubDate>
    </item>
    <item>
      <title>Img2Html_v0.144</title>
      <link>https://techbard.tistory.com/3219</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;자체 사용으로 부족한 부분 채워서 버전업 했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;627&quot; data-origin-height=&quot;381&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuXNpy/dJMcaiQx9JY/KngwLxQDiyBM60krgziaT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuXNpy/dJMcaiQx9JY/KngwLxQDiyBM60krgziaT1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuXNpy/dJMcaiQx9JY/KngwLxQDiyBM60krgziaT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuXNpy%2FdJMcaiQx9JY%2FKngwLxQDiyBM60krgziaT1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;627&quot; height=&quot;381&quot; data-origin-width=&quot;627&quot; data-origin-height=&quot;381&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>자작 프로그램</category>
      <author>techbard</author>
      <guid isPermaLink="true">https://techbard.tistory.com/3219</guid>
      <comments>https://techbard.tistory.com/3219#entry3219comment</comments>
      <pubDate>Tue, 21 Apr 2026 19:15:45 +0900</pubDate>
    </item>
    <item>
      <title>LSP 사용</title>
      <link>https://techbard.tistory.com/3218</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;LSP(Language Server Protocol)&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  LSP 서버 사용의 주요 효과&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. &lt;b&gt;정확한 코드 이해&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;LSP는 단순 문자열 검색이 아니라 &lt;b&gt;코드의 AST(Abstract Syntax Tree)와 타입 정보&lt;/b&gt;를 기반으로 분석합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;AI 에이전트가 함수, 변수, 클래스의 실제 정의와 참조를 구분할 수 있어 &lt;b&gt;오탐을 줄이고 정확한 결과&lt;/b&gt;를 제공합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. &lt;b&gt;IDE와 AI 에이전트의 분리&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;LSP는 원래 IDE와 언어 기능을 분리하기 위해 만들어졌습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;AI 코딩 에이전트도 LSP를 통해 &lt;b&gt;특정 IDE에 종속되지 않고 다양한 환경에서 동일한 언어 지원&lt;/b&gt;을 받을 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;예: VS Code, Vim, Cursor 같은 에디터에서 동일한 &amp;ldquo;언어 두뇌&amp;rdquo;를 공유 가능.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. &lt;b&gt;개발 생산성 향상&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&amp;ldquo;자동 완성&amp;rdquo;, &amp;ldquo;에러 진단&amp;rdquo;, &amp;ldquo;리팩토링 지원&amp;rdquo; 같은 기능을 AI가 LSP 서버를 통해 활용할 수 있어 &lt;b&gt;개발 속도와 품질이 개선&lt;/b&gt;됩니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;특히 대규모 코드베이스에서 AI가 더 빠르고 정확하게 맥락을 파악할 수 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. &lt;b&gt;에이전트 교체 용이성&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;최근에는 &lt;b&gt;Agent Client Protocol(ACP)&lt;/b&gt; 같은 확장 개념이 등장해, LSP처럼 AI 에이전트도 쉽게 교체할 수 있는 환경이 마련되고 있습니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;이는 개발자가 IDE를 바꾸지 않고도 &lt;b&gt;다양한 AI 코딩 에이전트를 손쉽게 전환&lt;/b&gt;할 수 있게 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;VS Code 용으로는 PureBasic LSP가 마켓 플레이스에 존재한다. 이를 opencode에서 사용하는 방법을 안내한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;동일 형식을 사용하므로 호환이 되며, opencode에서 json 등록하면 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;1. VS Code에 &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=meimingqi222.vscode-purebasic&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;플러그인&lt;/a&gt; 설치&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;2. powershell로 LSP 설치 경로 확인&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1776657861969&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Get-ChildItem &quot;$env:USERPROFILE\.vscode\extensions&quot; -Directory |
  Where-Object { $_.Name -like &quot;meimingqi222.vscode-purebasic-*&quot; } |
  Select-Object -ExpandProperty FullName&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;3. 윈도우 기준&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: oklab(0.159065 0.00000723451 0.00000317395 / 0.0329412); color: #0d0d0d; text-align: start;&quot;&gt;%USERPROFILE%\.config\opencode\opencode.json 편집&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1776657914685&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;$schema&quot;: &quot;https://opencode.ai/config.json&quot;,
  &quot;lsp&quot;: {
    &quot;purebasic&quot;: {
      &quot;command&quot;: [
        &quot;node&quot;,
        &quot;C:\\Users\\&amp;lt;USER&amp;gt;\\.vscode\\extensions\\meimingqi222.vscode-purebasic-0.0.4\\out\\server\\server.js&quot;,
        &quot;--stdio&quot;
      ],
      &quot;extensions&quot;: [&quot;.pb&quot;, &quot;.pbi&quot;, &quot;.pbp&quot;]
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. opencode 재시작&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5. 이제 *.pb 파일을 read 하고 나면 상단 &quot;상태&quot; 아이콘에서 LSP가 등록된 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;* 등록만 하면 안 된다, AGENTS.md에 지시를 하고 읽으라고 해야 한다.&lt;/p&gt;</description>
      <category>PureBasic</category>
      <category>PureBasic</category>
      <author>techbard</author>
      <guid isPermaLink="true">https://techbard.tistory.com/3218</guid>
      <comments>https://techbard.tistory.com/3218#entry3218comment</comments>
      <pubDate>Mon, 20 Apr 2026 13:07:49 +0900</pubDate>
    </item>
    <item>
      <title>img2html v0.132</title>
      <link>https://techbard.tistory.com/3216</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;스스로 쓰기 위해서 프로그램을 만들었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하위 폴더의 이미지 파일을 가지고 html을 생성해 이미지 갤러리로 사용할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;img2html.exe CLI 프로그램&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;img2html-gui.exe 프론트 엔드 (GUI 프로그램)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;README.* 가이드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LICENSE 라이선스&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;627&quot; data-origin-height=&quot;381&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZV8AI/dJMcabcH5fX/tGvBazUuEijrbA4pCIx3Tk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZV8AI/dJMcabcH5fX/tGvBazUuEijrbA4pCIx3Tk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZV8AI/dJMcabcH5fX/tGvBazUuEijrbA4pCIx3Tk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZV8AI%2FdJMcabcH5fX%2FtGvBazUuEijrbA4pCIx3Tk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;627&quot; height=&quot;381&quot; data-origin-width=&quot;627&quot; data-origin-height=&quot;381&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;윈도우 10, 11을 타겟합니다.&lt;/p&gt;</description>
      <category>자작 프로그램</category>
      <category>자작</category>
      <author>techbard</author>
      <guid isPermaLink="true">https://techbard.tistory.com/3216</guid>
      <comments>https://techbard.tistory.com/3216#entry3216comment</comments>
      <pubDate>Fri, 10 Apr 2026 22:37:18 +0900</pubDate>
    </item>
    <item>
      <title>ASCII art 타이틀 생성하기</title>
      <link>https://techbard.tistory.com/3215</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;개인적인 취미와 일부의 업무적 성격으로 윈도우 콘솔 프로그램을 많이 만들고 사용한다. 특히 윈도우 배치 파일도 자주 만들고 사용하는데 이것이 본디 콘솔 프로그램이다 보니 이과 감성의 출력을 보이는데 그나마 좀 있어 보이는 외관을 위해 배너를 출력하는데 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이른바 &lt;a style=&quot;background-color: #ffffff; color: #337ab7; text-align: start;&quot; href=&quot;https://patorjk.com/software/taag/&quot;&gt;Text to ASCII Art Generator&lt;/a&gt; 이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2025-07-29 12.03.40 patorjk.com 71f9ff7fbd3b.png&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;472&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/T4rke/btsPC5pYopS/6aZDYRccnrVTry9vYKZJD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/T4rke/btsPC5pYopS/6aZDYRccnrVTry9vYKZJD0/img.png&quot; data-alt=&quot;ANSI Shadow font&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/T4rke/btsPC5pYopS/6aZDYRccnrVTry9vYKZJD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FT4rke%2FbtsPC5pYopS%2F6aZDYRccnrVTry9vYKZJD0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1920&quot; height=&quot;472&quot; data-filename=&quot;2025-07-29 12.03.40 patorjk.com 71f9ff7fbd3b.png&quot; data-origin-width=&quot;1920&quot; data-origin-height=&quot;472&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ANSI Shadow font&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식으로 아스키 배너를 생성하기도 하고,&lt;/p&gt;
&lt;figure data-ke-type=&quot;image&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; data-ke-style=&quot;alignCenter&quot;&gt;&lt;span class=&quot;bar_progress&quot;&gt;&lt;/span&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ic.png&quot; data-origin-width=&quot;1387&quot; data-origin-height=&quot;1033&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZNgmP/btsPB0XhGsE/UkiCOqiDKaq6dHVkW7CdfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZNgmP/btsPB0XhGsE/UkiCOqiDKaq6dHVkW7CdfK/img.png&quot; data-alt=&quot;Modular font&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZNgmP/btsPB0XhGsE/UkiCOqiDKaq6dHVkW7CdfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZNgmP%2FbtsPB0XhGsE%2FUkiCOqiDKaq6dHVkW7CdfK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1387&quot; height=&quot;1033&quot; data-filename=&quot;ic.png&quot; data-origin-width=&quot;1387&quot; data-origin-height=&quot;1033&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Modular font&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 배치 파일에 출력해서 배너를 출력하기도 한다.&lt;/p&gt;</description>
      <category>PureBasic</category>
      <category>ASCII</category>
      <category>banner</category>
      <author>techbard</author>
      <guid isPermaLink="true">https://techbard.tistory.com/3215</guid>
      <comments>https://techbard.tistory.com/3215#entry3215comment</comments>
      <pubDate>Tue, 29 Jul 2025 12:07:55 +0900</pubDate>
    </item>
    <item>
      <title>Class</title>
      <link>https://techbard.tistory.com/3165</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot; data-ke-style=&quot;style3&quot;&gt;- A class is like a form or questionnaire.&lt;br /&gt;- An instance is like a form that has been filled out with information.&lt;br /&gt;- Just like many people can fill out the same form with their own unique information,&lt;br /&gt;many instances can be created from a single class.&lt;br /&gt;- Python class names are written in CapitalizedWords notation by convention.&lt;br /&gt;- Attributes created in .__init__() &amp;nbsp;are called instance attributes.&lt;br /&gt;- An instance attribute&amp;rsquo;s value is specific to a particular instance of the class.&lt;br /&gt;- On the other hand, class attributes are attributes that have the same value for all class instances.&lt;br /&gt;- You can define a class attribute by assigning a value to a variable &amp;nbsp;name outside of .__init__().&lt;br /&gt;- Use class attributes to define properties that should have the same value for every class instance.&lt;br /&gt;- Use instance attributes for properties that vary from one instance to another.&lt;br /&gt;- Instance methods are functions that are defined inside a class and can only be called from an instance of that class.&lt;br /&gt;- Just like .__init__(), an instance method&amp;rsquo;s first parameter is always self.&lt;br /&gt;- Methods like .__init__() &amp;nbsp;and .__str__() &amp;nbsp;are called dunder methods &amp;nbsp;because they begin and end with double underscores.&lt;br /&gt;- Inheritance is the process by which one class takes on the attributes and methods of another.&lt;br /&gt;- Newly formed classes are called child classes, and the classes that child classes are derived from are called&amp;nbsp;&lt;br /&gt;parent classes.&lt;br /&gt;- Inheritance is a way of creating a new class for using details of an existing class without modifying it.&lt;br /&gt;- Encapsulation is one of the key features of object-oriented programming.&lt;br /&gt;- Encapsulation refers to the bundling of attributes and methods inside a single class.&lt;br /&gt;- Polymorphism is, the same entity (method or operator or object) can perform different operations in different scenarios.&lt;br /&gt;- Uses of Inheritance:&lt;br /&gt;- Since a child class can inherit all the functionalities of the parent's class, this allows code reusability.&lt;br /&gt;- Once a functionality is developed, you can simply inherit it.&lt;br /&gt;- No need to reinvent the wheel.&lt;br /&gt;- This allows for cleaner code and easier to maintain.&lt;br /&gt;- Since you can also add your own functionalities in the child class, you can inherit only the useful functionalities and define other required features.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1745706061130&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# OOP
# Python uses classes to define new data types (objects).
# The Class composed of:
#
# Header statement +
# The class body which contain the nested def statement +
# define method that can be called on instances of the objects
# implemented by this class.

class MyClass():
    def show(self):
        print(&quot;Hi&quot;)

def check():
    x = MyClass()
    x.show()

check()&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1745707663777&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Class attributes
# - The attribute defined in the class is called &quot;class attributes&quot;
class MyClass():
    age = 21

print(MyClass.age)

# Instance Attributes
# - The attributes defined in the function is called &quot;instance attributes&quot;
class YourClass():
    def age(self):
        age = 21
        return age

yc = YourClass()
print(yc.age())&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1746357443861&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# The constructor
# - A class can define methods with special names.
# - These names begin and end with a double underscore.
#
# - One important special names is __init__, it's the
# constructor for a class.
#
# - It's called each time an instance of the class is created
# and Implementing this method in a class gives us a change to
# initialize each instance of our class.

class UserData():
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age

    def __str__(self):
        return f&quot;user name is {self.name} and has {self.age} years old.&quot;

    def __len__(self):
        return self.age

u1 = UserData(&quot;mike&quot;, 19)
u2 = UserData(&quot;anna&quot;, 21)

print(u1)
print(u2)

print(len(u1))
print(len(u2))

##outout
user name is mike and has 19 years old.
user name is anna and has 21 years old.
19
21&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1746574870155&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Class Inheritance
class UserData():
    age = 0
    name = None

    def __init__(self, age, name):
        self.age = age
        self.name = name

user_1 = UserData(age=40, name=&quot;David&quot;)

class PhoneNumber(UserData):
    def __init__(self, age, name, phone_num):
        super().__init__(age, name)
        self.phone_num = phone_num
    def __str__(self):
        return f&quot;user_name is {self.name}, has {self.age} years old, the phone_num is {self.phone_num}&quot;

user_1 = PhoneNumber(age=30, name=&quot;Sarah&quot;, phone_num=1234)
user_2 = PhoneNumber(age=20, name=&quot;Danny&quot;, phone_num=5678)

print(user_1)
print(user_2)

#output
user_name is Sarah, has 30 years old, the phone_num is 1234
user_name is Danny, has 20 years old, the phone_num is 5678&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1747465479779&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# what's the difference between regular and @classmethod?
# the difference that we can access class state and object instance or chcnge them
# in regular method.
# in the @classmethod we can access and use class state only but not the object instance.
# we use decorators to make our code easily readable.

class Fruit:
    def __init__(self, fruits):
        self.fruits = fruits

    def __repr__(self):
        return f&quot;Fruit({self.fruits})&quot;

    @classmethod
    def citrus(cls):
        return cls(['lemon', 'orange', 'tangerine'])

    @classmethod
    def grapes(cls):
        return cls(['green grapes', 'red grapes'])

    @staticmethod
    def season():
        return 'Citrus Fruites in winter, Grapes Fruits in summer'

f1 = Fruit.citrus()
f2 = Fruit.grapes()
f3 = Fruit.season()

print(f1)
print(f2)
print(f3)

#output
Fruit(['lemon', 'orange', 'tangerine'])
Fruit(['green grapes', 'red grapes'])
Citrus Fruites in winter, Grapes Fruits in summer&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1732878849743&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 가장 간단한 클래스 구조
class Person:
    def __init__(self, fname: str, lname: str) -&amp;gt; None:
        self.fname = fname
        self.lname = lname

    def get_full_name(self) -&amp;gt; str:
        return self.fname + &quot; &quot; + self.lname
    
    def __repr__(self) -&amp;gt; str:
        return f&quot;Person: {self.fname} {self.lname}&quot;

p1 = Person(&quot;Bob&quot;, &quot;Smith&quot;)
print(f&quot;{p1.fname}&quot;)
print(f&quot;{p1.lname}&quot;)
print(f&quot;{p1.get_full_name()}&quot;)
print(p1)

# 결과
Bob
Smith
Bob Smith
Person: Bob Smith&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1732604280158&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Car:
    def __init__(self, brand: str, wheels: int) -&amp;gt; None:
        self.brand = brand
        self.wheels = wheels
    def turn_on(self) -&amp;gt; None:
        print(f&quot;Turning on: {self.brand}&quot;)
    def turn_off(self) -&amp;gt; None:
        print(f&quot;Turning off: {self.brand}&quot;)
    def drive(self, km: float) -&amp;gt; None:
        print(f&quot;Driving: {self.brand} for {km}km&quot;)
    def describe(self) -&amp;gt; None:
        print(f&quot;{self.brand} is a car with {self.wheels} wheels&quot;)

def main() -&amp;gt; None:
    bmw: Car = Car(&quot;BMW&quot;, 4)
    bmw.turn_on()
    bmw.drive(10)
    bmw.describe()
    
    # It seems like we went through a lot of effort just for
    # a simple BMW.
    # Just like below, we created a whole new car that
    # has all the same functionality as that of the BMW
    # with some custom details.
    # So we don't have to rewrite any functionality for this Volvo
    # because we already created a blueprint that fits it quite nicely,
    # which means now we can also turn on the Volvo.
    # And that's why object oriented programming can be very appealing
    # also in Python.
    volvo: Car = Car(&quot;Volve&quot;, 6)
    volvo.turn_on()
    volvo.drive(100)
    volve.describe()

main()

# 결과
Turning on: BMW
Driving: BMW for 10km
BMW is a car with 4 wheels

Turning on: Volve
Driving: Volve for 100km
Volve is a car with 6 wheels&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731985484527&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 클래스 기초
class Dog:
    # 여기에는 클래스 공통의 속성이 온다. (class object attribute)
    # 어떤 인스턴스든 간에 모두 가지고 있는 속성 (same for any instance of a class)
    # self 키워드는 사용하지 않는다.
    # 이유는 self는 클래스의 특정한 인스턴스에 대한 참조이기 때문에
    species = 'mammal'
    
    def __init__(self, breed, name, spots): # 인스턴스 멤버 또는 속성의 정의
        self.breed = breed
        self.name = name
        self.spots = spots
    
    # 메서드: 클래스 바디에 정의된 함수
    # 인스턴스의 개별 속성을 사용하는 작업을 정의
    # 메서드는 클래스 내에 있는 함수로 인스턴스와 함께 작동
    # 메서드는 클래스와 연결되어 있지만, 함수는 클래스와 무관
    def bark(self, number): # 인스턴스 멤버로 초기화하지 않은 인자를 메서드에 넘길 수 있다.
        print(f&quot;Woof! my name is {self.name}, the num is {number}&quot;)

my_dog = Dog(breed='lab', name='Sammy', spots=False)

# 속성과 메서드의 차이점은 호출하는 방법에 있다.
# 속성은 괄호가 없다. 무언가를 실행하는 것이 아니기 때문이다.
# 단순히 인스턴스의 속성을 호출하는 것
# 이에 반해, 메서드는 실행이 필요하다. 실행을 위해서는 함수와 마찬가지로
# 인자가 필요하다.
print(my_dog.breed)
my_dog.bark(100)

# 결과
lab
Woof! my name is Sammy, the num is 100&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1732010880788&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Methods are functions bound to a class. (인스턴스 메서드의 이해)
# - Only Car objects can call the drive method. (with . notation)
# - Error if attempt to call drive function.
#
# self는 명시적으로 인스턴스를 전달한다는 표시를 남기도록 설계했다.
# 

class Car:
    def drive(self, miles):
        print(f&quot;The car drove {miles} miles.&quot;)

c = Car()
c.drive(100)

# 결과
The car drove 100 miles.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1733040915574&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# simple class &amp;amp; inheritance, method overriding
class Vehicle():
    def __init__(self, body_style):
        self.bodystyle = body_style

    def drive(self, speed):
        self.mode = &quot;driving&quot;
        self.speed = speed

class Car(Vehicle):
    def __init__(self, engine_type):
        super().__init__(&quot;Car&quot;)
        self.wheels = 4
        self.doors = 4
        self.engine_type = engine_type
        
    def drive(self, speed):
         super().drive(speed)
         print(&quot;Driving my&quot;, self.engine_type, &quot;car at&quot;, self.speed)

class Motocycle(Vehicle):
    def __init__(self, engine_type, has_sidecar):
        super().__init__(&quot;Motocycle&quot;)
        if has_sidecar:
            self.wheels = 3
        else:
            self.wheels = 2
        self.door = 0
        self.engine_type = engine_type

    def drive(self, speed):
         super().drive(speed)
         print(&quot;Driving my&quot;, self.engine_type, &quot;motocycle at&quot;, self.speed)

car1 = Car(&quot;gas&quot;)
car2 = Car(&quot;electric&quot;)
mc1 = Motocycle(&quot;gas&quot;, True)

print(mc1.wheels)
print(car1.engine_type)
print(car2.doors)

# 결과
3
gas
4
Driving my gas car at 30
Driving my electric car at 40
Driving my gas motocycle at 50&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731934455424&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Classes &amp;amp; Instances: Combine Methods &amp;amp; Data

class Point():
    def getX(self):
        return self.x

    # When we call this method p1.getX
    # then one of the differences
    # between methods and functions,
    # remember, methods belong to a class.
    # So whenever we call a method we have
    # a particular actually instance that
    # we're calling that method on.
    # So in this case we aren't just calling
    # getX(), we're saying p1.getX().
    # And so when we say p1.getX() what happens
    # is that we're automatically passing in this
    # instance p1 as self when we call getX().
    # So when we say p1.getX(), even if we have no
    # arguments here and what Pytghon does is it automatically
    # takes what's befor the dot or the instance that we're
    # calling this on and it passes it in, As the value of self.
    # If we had instead replace that code to say, Print p2.getX().
    # Then what would happen is, instead of passing in p1,
    # then we would be passing in p2.
    # So we would've said p2.getX() would pass in p2 as the value
    # for the self.  

p1 = Point()
p2 = Point()

p1.x = 5
p2.x = 10

print(p1)
print(f&quot;Are p1 and p2 equals? {p1 == p2}&quot;)

print(p1.getX())
print(p2.getX())

# 결과
&amp;lt;__main__.Point object at 0x0000020C547C6900&amp;gt;
Are p1 and p2 equals? False
5
10&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731966908527&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Sorting Lists of Instances

class Fruit():
    def __init__(self, name, price):
        self.name = name
        self.price = price
    def getName(self):
        return self.name
    def getPrice(self):
        return self.price
    def __str__(self):
        return f'({self.name}, {self.price})'

L = [
    Fruit(&quot;cherry&quot;, 10),
    Fruit(&quot;apple&quot;, 5),
    Fruit(&quot;blueberry&quot;, 20)
    ]

# So we need to be able to tell sorted how to compare
# two fruit items by passing in a value for key.
# Now remember that key is expecting a function as it's argument,
# so what I want to pass into key is a function that tell us what
# we want to sort by.
# print(sorted(L, key=Fruit.getName))

# for f in sorted(L, key=Fruit.getName):
    # print(f)

# Or

for f in sorted(L, key=lambda x: x.getName()):
    print(f'sorted by name: {f}')

print(&quot;&quot;)

for f in sorted(L, key=lambda x: x.getPrice()):
    print(f'sorted by price: {f}')

# 결과
sorted by name: (apple, 5)
sorted by name: (blueberry, 20)
sorted by name: (cherry, 10)

sorted by price: (apple, 5)
sorted by price: (cherry, 10)
sorted by price: (blueberry, 20)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731936739779&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Creating Instances from Data

city_names = ['Detroit', 'Ann Arbor', 'Pittsburgh',
            'Mars', 'New York']
populations = [680250, 117070, 304391, 1683, 8406000]
states = ['MI', 'MI', 'PA', 'PA', 'NY']

city_tuples = zip(city_names, populations, states)

# print(list(city_tuples))

class City:
    def __init__(self, n, p, s):
        self.name = n
        self.population = p
        self.state = s
    def __str__(self):
        return f'{self.name}, {self.state} (pop: {self.population})'

# cities = []
# for city_tup in city_tuples:
    # name, pop, state = city_tup
    # city = City(name, pop, state) # instance of the City class
    # cities.append(city)

cities = [City(n, p, s) for (n, p ,s) in city_tuples]
# or
# cities = [City(*tup) for tup in city_tuples]

for city in cities:
    print(city)

# 결과
Detroit, MI (pop: 680250)
Ann Arbor, MI (pop: 117070)
Pittsburgh, PA (pop: 304391)
Mars, PA (pop: 1683)
New York, NY (pop: 8406000)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731966060555&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Point():
    def __init__(self, initX, initY):
        self.x = initX
        self.y = initY
    def getX(self):
        return self.x
    def getY(self):
        return self.y
    def __str__(self):
        return f'(Point: {self.x}, {self.y})'
    def __add__(self, otherPoint):
        return Point(self.x + otherPoint.x, self.y + otherPoint.y)
    def __sub__(self, otherPoint):
        return Point(self.x - otherPoint.x, self.y - otherPoint.y)
    def halfway(self, otherPoint):
        midX = (self.x + otherPoint.x)/2
        midY = (self.y + otherPoint.y)/2
        return Point(midX, midY)

p1 = Point(1, 1)
p2 = Point(2, 2)

print(p1 + p2)
print(p1 - p2)

mid = p1.halfway(p2)
print(mid)
print(mid.getX())
print(mid.getY())

# 결과
(Point: 3, 3)
(Point: -1, -1)
(Point: 1.5, 1.5)
1.5
1.5&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731994284769&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 샘플 클래스
# 거리, 기울기 구하는 공식 적용
class Line:
    def __init__(self, coord1, coord2):
        self.coord1 = coord1 # tuple
        self.coord2 = coord2
    
    def distance(self):
        (x1, y1) = self.coord1
        (x2, y2) = self.coord2
        return ((x2-x1)**2 + (y2-y1)**2)**0.5

    def slope(self):
        (x1, y1) = self.coord1
        (x2, y2) = self.coord2
        return (y2-y1)/(x2-x1)

c1 = (3, 2)
c2 = (8, 10)

li = Line(c1, c2)
print(li.distance())
print(li.slope())

# 결과
9.433981132056603
1.6&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731995275012&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 동일한 두 점 거리와 기울기 구하는 공식을 가지고
# 리스트로 좌표 두 개가 주어질 때
# 그런데 해보니 리스트로 던져도 언패킹이 된다.
# 또한 구현하기 나름인데, 사실 그냥 멤버 초기화 없이 메서드에 값을 넣어도 된다.
class Line:
    def __init__(self, coor1, coor2):
        self.coor1 = coor1
        self.coor2 = coor2

    def distance(self):
        x1 = self.coor1[0]
        y1 = self.coor1[1]
        x2 = self.coor2[0]
        y2 = self.coor2[1]
        return ((x2-x1)**2 + (y2-y1)**2)**0.5

    def slope(self):
        x1 = self.coor1[0]
        y1 = self.coor1[1]
        x2 = self.coor2[0]
        y2 = self.coor2[1]
        return (y2-y1)/(x2-x1)

c1 = [3, 2]
c2 = [8, 10]

li = Line(c1, c2)
print(li.distance())
print(li.slope())

# 결과
9.433981132056603
1.6&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731996415487&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 은행 계좌 예제 클래스
class Account:
    def __init__(self, owner, balance):
        self.owner = owner
        self.balance = balance
        
    def withdraw(self, amount):
        if amount &amp;lt; 0:
            print(&quot;not a negative one.&quot;)
        elif amount &amp;gt; self.balance:
            print(&quot;You don't have enough balance.&quot;)
        else:
            self.balance -= amount
    
    def deposit(self, amount):
        if amount &amp;lt; 0:
            print(&quot;not a negative one.&quot;)
        else:
            self.balance += amount
    
    def __str__(self):
        return f'Hello {self.owner}, your balance is {self.balance}'

ac1 = Account('Jose', 100)
print(ac1)
ac1.deposit(50)
print(ac1)
ac1.withdraw(75)
print(ac1)
ac1.withdraw(80)
print(ac1)

# 결과
Hello Jose, your balance is 100
Hello Jose, your balance is 150
Hello Jose, your balance is 75
You don't have enough balance.
Hello Jose, your balance is 75&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731278230293&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Creating New Types
# - class keyword followed by:
# -- the name of the type and a colon
# -- similar to user-defined functions
# - Create the Car type

class Car:
    pass

# 객체를 생성하지 않았기 때문에 'type' 이다.
print(type(Car))

# built-in 'type' 이다.
print(type(int))

# t는 'str' 클래스의 멤버이다.
t = 'text'
print(type(t))

# Creating a Car instance
# - Car is no an instance
# - Car is a constructor that construc new Car instances
# -- Just like int constructs new integers and str constructs new strings
# - Must call Car() to create an instances

# 일종의 factory 이다.
my_car = Car()

# Factories
# Think of Car as a factory that constructs new Car instances
# This one factory can create any number of instances

print(type(int(1)))

print(type(my_car))
# &amp;lt;class '__main__.Car'&amp;gt;
# __main__ 은 이 모듈의 이름이다.

# Terminology: my_car is an instance of the Car class
# -- We created an instance of the Car class.
# An instance is a single member of the class.
# Another way of saying it - &quot;we instantiated the Car class by creating the my_car instance&quot;
# &quot;We constructed a new instance of the Car class&quot;

# 결과
&amp;lt;class 'type'&amp;gt;
&amp;lt;class 'type'&amp;gt;
&amp;lt;class 'str'&amp;gt;
&amp;lt;class 'int'&amp;gt;
&amp;lt;class '__main__.Car'&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731284847534&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# What is the parameter self doing here?
# - The first parameter refers to the calling instances
# - Instance is implicitly passed the method.

class Car:
    def drive(self, kms):
        print(f'The car drove {kms} kilometers.')

# Create new instance and call drive method
# my_car instance가 Car 클래스의 self에 암시적으로 전달된다.
# 이렇게 self를 쓰는 이유는 클래스 내부에서만 접근 가능한 메서드와
# instance에서 접근 가능한 메서드를 구분하기 위함으로 생각된다.
my_car = Car()
my_car.drive(10)
# 실상은 my_car.drive(my_car, 10) 이다. 내부적으로 생략해 처리한다.
# 즉, 인스턴스 메서드에 인스턴스 자체를 함께 던지는 개념이다.
# 결국 여러 인스턴스가 존재하며 그 각각의 인스턴스를 클래스 내부의
# 메서드에 함께 던져서 각각의 메서드를 사용할 수 있다.

# 결과
The car drove 10 kilometers.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731286578540&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Object-oriented Programming
# - Creating classes, instantiating them, calling methods and
# retrieving attributes are all part of a programming paradigm
# called object-oriented programming.
# - In Python, every variable name created is an object.
# - All objects have types that are defined by their classes.


# Initializing an instance
# - Initialize instances in a specific and customized way.
# - Like choosing a car's color, number or seats, horsepower, transmission, and
# any other features.
# - No customization for current Car class - all instances are the same

# Define __init__ method
# - Gets called implicitly when consteucted

# __init__ 메서드를 이용하는 것은 결국 클래스를 커스토마이징하는 것
# 동일한 클래스를 커스토마이징해서 원하는 목적에 맞게 사용한다.
class Car:
    def __init__(self, color, price, transmission, make, model, mileage):
        self.color = color
        self.price = price
        self.transmission = transmission
        self.make = make
        self.model = model
        self.mileage = mileage
    
    def drive(self, kms):
        print(f'The car drove {kms} kilometers.')

my_car = Car(color='Red', price=80_000, transmission='Manual',
            make='Tesla', model='S3', mileage=0)

print(my_car.color)
print(my_car.model)

# 결과
Red
S3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1732016528456&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Object-oriented Programming
# - Creating classes, instantiating them, calling methods and
# retrieving attributes are all part of a programming paradigm called
# object-oriented programming.
# - In Python, every variable name is an object.
# - All objects have types that are defined by their classes.

# Initializing an instance
# - Lnitialize instances in a specific and customized way.
# - No customization for current Car class - all instances are the same


# 클래스 멤버 사용, 인스턴스 멤버와 메서드 인자를 함께 사용
class Car:
    depreciation = 0.25
    
    def __init__(self, color, price, make, model, mileage):
        self.color = color
        self.price = price
        self.make = make.upper()
        self.model = model
        self.mileage = mileage
        
    def drive(self, miles):
        self.mileage += miles
        self.price -= miles * 0.5
        print(f&quot;Your {self.make} {self.model} car drove {miles} miles.&quot;)
        print(f&quot;It has {self.mileage} miles.&quot;)
        print(f&quot;Its current price is {self.price}&quot;)
        
    def future_lifetime(self):
        return self.price / Car.depreciation

my_car = Car(color='silver', price=35000, mileage=0, make='Tesla', model='S3')
my_car.drive(10)
my_car.drive(100)
my_car.drive(10_000)
my_car.future_lifetime()

# 결과
Your TESLA S3 car drove 10 miles.
It has 10 miles.
Its current price is 34995.0
Your TESLA S3 car drove 100 miles.
It has 110 miles.
Its current price is 34945.0
Your TESLA S3 car drove 10000 miles.
It has 10110 miles.
Its current price is 29945.0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731290586645&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Object composition
# - When instrances have attributes that refer to instances of other classes
# - Change Car class so that a Person instance created in constructor

class Person:
    def __init__(self, name):
        self.name = name
        
    def greet(self):
        print(f'Hello, my name is {self.name}')

class Car:
    def __init__(self, color, price, model, mileage, person_name):
        self.color = color
        self.price = price
        self.model = model
        self.mileage = mileage
        # Person class instantiated here
        self.driver = Person(person_name)
        self.depreciation = .5

    def drive(self, miles):
        self.mileage += miles
        self.price -= miles * self.depreciation
        print(f'Your {self.model} drove {miles} miles')
        print(f'Total miles driven: {self.mileage}')
        print(f'Current value of car: {self.price}')

    def future_lifetime(self):
        return self.price / self.depreciation

car1 = Car(color='silver', price=35_000, mileage=0, model='Tesla', person_name='Bob')

car1.drive(10000)
ret = car1.future_lifetime()
print(f'future lifetime: {ret}')
car1.driver.greet()

# 결과
Your Tesla drove 10000 miles
Total miles driven: 10000
Current value of car: 30000.0
future lifetime: 60000.0
Hello, my name is Bob&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731970383969&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 상속과 합성

class Book:
    def __init__(self, title, author):
        self.title = title
        self.author = author
    def __str__(self):
        return f'{self.title} by {self.author}'

class PaperBook(Book):
    def __init__(self, title, author, numPages):
        super().__init__(title, author)
        self.numPages = numPages
    def __str__(self):
        return f'{self.title} by {self.author} ({self.numPages}p)'

class Ebook(Book):
    def __init__(self, title, author, fileSize):
        super().__init__(title, author)
        self.fileSize = fileSize
    def __str__(self):
        return f'{self.title} by {self.author} ({self.fileSize}MB)'
# 여기는 상속

class Library:
    def __init__(self):
        self.books = []
    def addBook(self, book):
        self.books.append(book)
    def getNumBooks(self):
        return len(self.books)
# 여기는 composition (a library contains or is composed of a list of books.)

p1 = PaperBook('The Odyssey', 'Homer', 499)
print(p1)

p2 = Ebook('The Odyssey', 'Homer', 2)
print(p2)

aLibrary = Library()
aLibrary.addBook(p1)
aLibrary.addBook(p2)
print(f'There (is)are {aLibrary.getNumBooks()} book(s).')

# 결과
The Odyssey by Homer (499p)
The Odyssey by Homer (2MB)
There (is)are 2 book(s).&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1730963819737&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 클래스 파라미터의 사용

class BaseStudent:
    def __init__(self, name, age, marks):
        self.name = name
        self.age = age
        self.marks = marks

    def display(self):
        print(f'Hi {self.name}')
        print(f'Your age: {self.age}')
        print(f'Your marks: {self.marks}')

name = input('enter your name -&amp;gt; ')
age = int(input('enter your age -&amp;gt; '))
marks = int(input('enter your marks -&amp;gt; '))

s1 = BaseStudent(name, age, marks)
s1.display()

class ParamStudent:
    def __init__(self, n, a, *m):
        self.n = n
        self.a = a
        self.m = m

    def display(self):
        print('Hi', self.n)
        print('Your age', self.a)
        print('Your marks', self.m)

s2 = ParamStudent('kelly', 22, 70, 65, 90)
s2.display()

class KeyValueStudent:
    def __init__(self, n, a, **m):
        self.n = n
        self.a = a
        self.m = m
    
    def display(self):
        print('Hi', self.n)
        print('Your age', self.a)
        print('Your marks', self.m)

s3 = KeyValueStudent('John', 19, scienc = 95, english = 70, maths = 88)
s3.display()

# 결과
enter your name -&amp;gt; mike
enter your age -&amp;gt; 20
enter your marks -&amp;gt; 88
Hi mike
Your age: 20
Your marks: 88
Hi kelly
Your age 22
Your marks (70, 65, 90)
Hi John
Your age 19
Your marks {'scienc': 95, 'english': 70, 'maths': 88}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731298493599&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import random

# Single Responsibility Principle
# - Why two methods roll and _roll
# - SRP each component of a program responsibility over one task
# - _roll completes one roll of all dice
# - roll rolls n number of times
# - encourages user to call roll

# 간단한 클래스
# public, private method
class Dice:
    def __init__(self, sides=6, num=2, verbose=False):
        self.sides = sides
        self.num = num
        self.verbose = verbose
        self.rolls = []
        self.sums = []
    
    # private method
    def _roll(self):
        '''
        One complete roll of all dice
        '''
        curr_roll = tuple(random.randint(1, self.sides) for i in range(self.num))
        self.rolls.append(curr_roll)
        self.sums.append(sum(curr_roll))
        if self.verbose:
            print(f'Rolling {self.num} {self.sides}-sided dice: {curr_roll}')

    # public method
    def roll(self, n=1):
        '''
        Roll all dice n number of times
        '''
        for i in range(n):
            self._roll()

dice = Dice(sides=6, num=4, verbose=True)
print(type(dice))
dice.roll(3)
print(dice.sums)

# 결과
&amp;lt;class '__main__.Dice'&amp;gt;
Rolling 4 6-sided dice: (5, 4, 5, 1)
Rolling 4 6-sided dice: (1, 3, 5, 3)
Rolling 4 6-sided dice: (2, 1, 5, 6)
[15, 12, 14]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731308593917&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Another big benefit of classes is that we
# can reuse them. We've already used many
# built-in types, including the string, the list,
# the dictionary, and the various numeric types.
# Instead of reimplementing the functionality provided by these types,
# we could focus on creating our programs.

class Pokemon:
    def __init__(self, name: str, max_armor: int, max_hit: int):
        self.name = name
        self.armor = max_armor
        self.hit_points = max_hit
        
    def attack(self):
        print(f'{self.name} attacks')
    
    def defend(self):
        print(f'{self.name} defends itself')
 
 
pikachu = Pokemon('Pikachu', 100, 1000)
pikachu.attack()
pikachu.defend()

snolax = Pokemon('Snorlax', 75, 900)
snolax.attack()

# 결과
Pikachu attacks
Pikachu defends itself
Snorlax attacks&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1683081253373&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;### 클래스 생성자, 클래스 메소드

class myClass:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def myMethod(self):
        print(f'This is myMethod -&amp;gt; x: {self.x}, y: {self.y}')

def main():
    mc = myClass(1, 2)
    mc.myMethod()
    pass

if __name__ == '__main__':
    main()

# 결과
This is myMethod -&amp;gt; x: 1, y: 2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1730985944921&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class PrivateMemberExample:
    def __init__(self):
        self.x = 1
        self._y = 2
        self.__z = 3

c1 = PrivateMemberExample()
print(f'멤버에 언더스코어 없는 self.x 접근 가능: {c1.x}')

c2 = PrivateMemberExample()
print(f'멤버에 1개 언더스코어 있는 self._y 접근 가능: {c2._y}')

# 결과
멤버에 언더스코어 없는 self.x 접근 가능: 1
멤버에 1개 언더스코어 있는 self._y 접근 가능: 2

# c3 = PrivateMemberExample()
# print(f'멤버에 2개 언더스코어 있는 self.__z 접근 불가: {c3.__z}')

# 결과
AttributeError: 'PrivateMemberExample' object has no attribute '__z'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1683090377488&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;### 스태틱 필드의 사용 (사실 함수와 다를게 없다)

class UseStaticField:
    result = []

    def __init__(self, *args):
        ret = []
        c = random.randint(0, 1)
        for arg in args:
            if c == 0 and arg % 2 == 0:
                ret.append(arg)
            if c == 1 and arg % 2 == 1:
                ret.append(arg)
        UseStaticField.result = ret

    def getResult(self):
        return UseStaticField.result

def main():
    u1 = UseStaticField(1, 2, 3, 4, 5)
    print(u1.getResult())

    u2 = UseStaticField(0, 7, 10, 23)
    print(u2.getResult())

    pass

if __name__ == '__main__':
    main()

#결과 1
[2, 4]
[0, 10]

#결과 2
[1, 3, 5]
[7, 23]

### 클래스 메쏘드로 쪼갠 버전

import random

class ChoiceNums:
    inList = []
    outList = []

    def __init__(self, *args):
        ChoiceNums.inList.clear()
        for arg in args:
            ChoiceNums.inList.append(arg)

    def getEvens(self):
        ChoiceNums.outList.clear()
        for e in ChoiceNums.inList:
            if e % 2 == 0:
                ChoiceNums.outList.append(e)
        return ChoiceNums.outList

    def getOdds(self):
        ChoiceNums.outList.clear()
        for e in ChoiceNums.inList:
            if e % 2 == 1:
                ChoiceNums.outList.append(e)
        return ChoiceNums.outList

def main():
    u1 = ChoiceNums(1, 2, 3, 4, 5)
    print('even:', u1.getEvens())
    print('odd:', u1.getOdds())

    print('\t')

    u2 = ChoiceNums(0, 7, 10, 23)
    print('even:', u2.getEvens())
    print('odd:', u2.getOdds())

    pass

if __name__ == '__main__':
    main()

# 결과
even: [2, 4]
odd: [1, 3, 5]
	
even: [0, 10]
odd: [7, 23]

### 객체 안의 데이터를 사용한 버전

class selectNums:

    def __init__(self, *args):
        self.args = args
        self.ret = []

    def getEvens(self):
        self.ret.clear()
        for e in self.args:
            if e % 2 == 0:
                self.ret.append(e)
        return self.ret

    def getOdds(self):
        self.ret.clear()
        for e in self.args:
            if e % 2 == 1:
                self.ret.append(e)
        return self.ret

def main():
    u1 = selectNums(1, 2, 3, 4, 5)
    print('even:', u1.getEvens())
    print('odd:', u1.getOdds())

    print('\t')

    u2 = selectNums(0, 7, 10, 23)
    print('even:', u2.getEvens())
    print('odd:', u2.getOdds())

    pass

if __name__ == '__main__':
    main()

# 결과
even: [2, 4]
odd: [1, 3, 5]
	
even: [0, 10]
odd: [7, 23]

### 리스트 컴프리헨션으로 객체 내부에 리스트 유지 X

class selectNums:
    def __init__(self, *args):
        self.args = args

    def getEvens(self):
        return [e for e in self.args if e % 2 == 0]

    def getOdds(self):
        return [e for e in self.args if e % 2 == 1]

def main():
    u1 = selectNums(1, 2, 3, 4, 5)
    print('even:', u1.getEvens())
    print('odd:', u1.getOdds())

    print('\t')

    u2 = selectNums(0, 7, 10, 23)
    print('even:', u2.getEvens())
    print('odd:', u2.getOdds())

    pass

if __name__ == '__main__':
    main()

# 결과
even: [2, 4]
odd: [1, 3, 5]
	
even: [0, 10]
odd: [7, 23]&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1683110624139&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 클래스 속성과 인스턴스 속성 구별
class SProperty:
    shared_ls = [] # 클래스 속성
    def __init__(self, n):
        self.shared_ls.append(n) # self로 지정해도 접근 가능

class PProperty:
    def __init__(self):
        self.private_ls = [] # 인스턴스 속성
    def put(self, n):
        self.private_ls.append(n)

def main():
    s1 = SProperty('1')
    s2 = SProperty('2')

    print('-&amp;gt; 클래스 속성은 모든 인스턴스에 공유')
    print('s1:', s1.shared_ls)
    print('s2:', s2.shared_ls)

    print('\t')

    p1 = PProperty()
    p1.put('3')
    p2 = PProperty()
    p2.put('4')

    print('-&amp;gt; 인스턴스 속성은 인스턴스별로 독립')
    print('p1:', p1.private_ls)
    print('p2:', p2.private_ls)

    pass

if __name__ == '__main__':
    main()

# 결과
-&amp;gt; 클래스 속성은 모든 인스턴스에 공유
s1: ['1', '2']
s2: ['1', '2']
	
-&amp;gt; 인스턴스 속성은 인스턴스별로 독립
p1: ['3']
p2: ['4']&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1734667450749&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Book:
    book_count = 0 # keep track of how many books are created.
    def __init__(self, title, author, pages):
        self.title = title
        self.author = author
        self.pages = pages
        Book.book_count += 1

    # Instance Method
    def get_description(self):
        return f&quot;'{self.title}' by {self.author}, {self.pages}&quot;

    # Class Method
    @classmethod
    def get_book_count(cls):
        return cls.book_count
    
    # Static Method (Utility functions)
    @staticmethod
    def is_small_book(page_count) -&amp;gt; bool:
        return page_count &amp;lt; 200

book = Book(title='The book', author='John Doe', pages=200)
print(book.get_description())
print(Book.get_book_count())
print(Book.is_small_book(150))
print(Book.is_small_book(300))

# Output:
'The book' by John Doe, 200
1
True
False&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1683698028218&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 기본적인 상속의 예
class Person():
    # 생성자
    def __init__(self, name):
        self.name = name

    # get name
    def getName(self):
        return self.name

    # employee 객체인지 체크
    def isEmployee(self):
        return False

class Employee(Person):
    def isEmployee(self):
        return  True

def main():
    e1 = Person('Big Boss')
    print(e1.getName(), e1.isEmployee())

    e2 = Employee('Ant worker')
    print(e2.getName(), e2.isEmployee())

if __name__ == '__main__':
    main()
 
 #결과
Big Boss False
Ant worker True&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731026790110&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Super - Sub class 상속 관계의 예
# Super에는 멤버와 setter / getter를 두고
# Sub에 메서드를 유지

class Polygon:
    __width = None
    __heigh = None
    
    def set_value(self, w, h):
        self.__width = w
        self.__heigh = h
    
    def get_width(self):
        return self.__width
    
    def get_height(self):
        return self.__heigh
    
class Square(Polygon):
    def area(self):
        return self.get_width() * self.get_height()

class Triangle(Polygon):
    def area(self):
        return self.get_width() * self.get_height() * 1/2

s1 = Square()
s1.set_value(8, 15)
print(s1.area())

t1 = Triangle()
t1.set_value(5, 10)
print(t1.area())

# 결과
120
25.0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731035400038&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# In Python, super() built-in has two major use cases:
# - Allows us to avoid using base class explicitly
# - Working with Multiple Ingeritance

# 상속 관계일 때 super class의 메서드 직접 호출
class SuperClass:
    def __init__(self, in_msg):
        print('super class __init__ called.', in_msg)

class SubClass(SuperClass):
    def __init__(self, msg):
        print('sub class __init__ called.')
        # SuperClass.__init__(self, msg)
        super().__init__(msg)
        # 수퍼 클래스를 명시해도 되지만,
        # super() 함수를 호출해도 된다.
        # 단, 이 경우 self 객체 전달은 하지 않는다.

sub1 = SubClass(&quot;I'm SUB&quot;)

# 결과
sub class __init__ called.
super class __init__ called. I'm SUB&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731050795439&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Composition

# content is part of container
# Salary is part of Employee
# - Employee is my container and Salary is my content.
# - Container are destroyed then the content will be of no use.
# - This is why composition has strong relationship.
# - Imply that means they are both depending on each other.

# 반면에 aggregation의 경우 약한 결합이며
# 아래의 예에서 객체를 생성할 때 Salary 객체를 생성해
# 그 객체를 Employee 객체를 만들 때 넘겨주면 된다.
# 다만, 그 경계와 구분은 모호하며 상황이나 문제에 따라 사용한다.
#
# self.final_salary = Salary(pay, reward) -&amp;gt;
# self.final_salary = salary_object

# salary_object = Salary(1000, 100))

# class Employee:
    # def __init__(self, name, position, salary_object)

class Salary:
    def __init__(self, pay, reward):
        self.pay = pay
        self.reward = reward
    
    def annual_salary(self):
        return self.pay * 12 + self.reward

class Employee:
    def __init__(self, name, position, pay, reward):
        self.name = name
        self.position = position
        self.final_salary = Salary(pay, reward)
        # self.final_salary가 객체임에 유의

    def get_final_salary(self):
        return self.final_salary.annual_salary()

emp = Employee(&quot;mike&quot;, &quot;Py dev&quot;, 1000, 100)
print(emp.get_final_salary())

# 결과
12100&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1683710472690&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;### 다형성 함수의 간단한 예
def myAdd(x, y, z = 0):
    return x + y + z

def main():
    print(myAdd(1, 2))
    print(myAdd(1, 2, 3))

if __name__ == '__main__':
    main()

#결과
3
6&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1683712311634&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;### 클래스 메서드의 다형성
class hero():
    def say(self):
        print(&quot;I'm hero.&quot;)

class villain():
    def say(self):
        print(&quot;I'm villain!&quot;)

def main():
    h = hero()
    v = villain()

    for e in (h, v):
        e.say()

if __name__ == '__main__':
    main()

#결과
I'm hero.
I'm villain!&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1683717670179&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;### 클래스 메서드와 스태틱 메서드의 사용
&quot;&quot;&quot;
1. We generally use the class method to create factory methods. Factory methods return class objects ( similar to a constructor ) for different use cases.
2. We generally use static methods to create utility functions.
&quot;&quot;&quot;
import datetime

class Person:
    staticVar = 0
    def __init__(self, name, age):
        Person.staticVar += 1
        self.name = name
        self.age = age

    def getName(self):
        return self.name

    @staticmethod
    def getObjCount():
        return Person.staticVar

    @classmethod
    def fromBirthYear(cls, name, year):
        return cls(name, datetime.date.today().year - year)

def main():
    p1 = Person('sally', 21)
    p2 = Person.fromBirthYear('sally', 1996)
    p3 = Person('danny', 30)

    print(p1.getName(), p1.age)
    print(p2.getName(), p2.age)
    print(p3.getName(), p3.age)

    print('\t')

    print('obj count:', Person.getObjCount())

if __name__ == '__main__':
    main()

#결과
sally 21
sally 27
danny 30
	
obj count: 3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1732624410225&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# class method

class Car:
    LIMITER: int = 160
    # 인스턴스 메서드가 이 클래스 멤버를 변경해도 자신의 인스턴스
    # 내의 멤버만 변경되고 다른 객체의 클래스 멤버는 변경되지 않는다.
    # 그러나, 클래스 메서드가 클래스 멤버를 변경하면 모든 인스턴스의
    # 클래스 멤버가 동시에 변경된다. (공용 변수처럼)

    def __init__(self, brand: str, max_speed: int) -&amp;gt; None:
        self.brand = brand
        self.max_speed = max_speed
        
    @classmethod
    def change_limit(cls, new_limit: int) -&amp;gt; None:
        cls.LIMITER = new_limit

def main() -&amp;gt; None:
    c1: Car = Car(&quot;BMW&quot;, 160)
    c2: Car = Car(&quot;Porsche&quot;, 200)

    c1.change_limit(170)
    print(f&quot;{c1.brand} limit: {c1.LIMITER} km&quot;)
    print(f&quot;{c2.brand} limit: {c2.LIMITER} km&quot;)

    c2.change_limit(200)
    print(f&quot;{c1.brand} limit: {c1.LIMITER} km&quot;)
    print(f&quot;{c2.brand} limit: {c2.LIMITER} km&quot;)

main()

# 결과
BMW limit: 170 km
Porsche limit: 170 km
BMW limit: 200 km
Porsche limit: 200 km&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1732772977643&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Doc Strings
# Sphinx Markup
# 기본적인 클래스 사용 예

class User:
    &quot;&quot;&quot;
    Base class for creating user.
    &quot;&quot;&quot;
    
    def __init__(self, user_id: int) -&amp;gt; None:
        self.user_id = user_id
    
    def show_id(self) -&amp;gt; None:
        &quot;&quot;&quot;Print the user_id as an int&quot;&quot;&quot;
        print(self.user_id)

def user_exist(user: User, database: set[User]) -&amp;gt; bool:
    &quot;&quot;&quot;
    Checks if a user is inside a database.
    :param user: the user to check for
    :param database: the database to check inside
    :return: bool
    &quot;&quot;&quot;
    return user in database


def main() -&amp;gt; None:
    user: User = User(0)
    user.show_id()
    
    bob: User = User(0)
    anna: User = User(1)
    
    database: set[User] = {bob, anna}
    
    if user_exist(bob, database):
        print(f&quot;User exists in database!&quot;)
    else:
        print(&quot;No user found...&quot;)

main()

# 결과
0
User exists in database!&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1732801388138&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 간단 객체 관리
# 최대한 type hinting 사용
class Person:
    _counter:int = 0
    _instances: list[any] = []

    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age
        self._instances.append(self)
        Person._counter += 1

    def __repr__(self) -&amp;gt; str:
        return f&quot;{self.name}={self.age} old&quot;

    @classmethod
    def get_instances(cls) -&amp;gt; list[any]:
        return cls._instances

    @classmethod
    def get_counter(cls) -&amp;gt; int:
        return cls._counter

def main() -&amp;gt; None:
    Bob: Person = Person('Bob', 29)
    Sally: Person = Person('Sally', 19)
    John: Person = Person('John', 39)
    Mike: Person = Person('Mike', 49)

    person_dict: dict[str, int] = {}
    for person in Person.get_instances():
        person_dict[person.name] = person.age
    print(person_dict)
    print(f&quot;The count of Person object: {Person.get_counter()}&quot;)

main()

# 결과
{'Bob': 29, 'Sally': 19, 'John': 39, 'Mike': 49}
The count of Person object: 4&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1732805025659&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# @dataclass
# In Pyhton, a data class is a class that is designed to only old data values.
# They aren't different from regular classes, but they usually
# don't have any other methods.
# They are typically used to store information that will
# be passed between different parts of a program of a system

# However, when creating classes to work only as data containers,
# writing the __init__ method repeatedly can generate a great
# amount of work and potential errors.

# The dataclasses module, a feature introduced in Python 3.7,
# provides a way to create data classes in a simpler manner
# without the need to write methods.

# Using a data class can save us a lot of trouble
# when we just want to create classes that contain data.
from dataclasses import dataclass

@dataclass
class Coin:
    name: str
    value: float
    id: str

def main():
    bitcoin1: Coin = Coin(&quot;Bitcoin&quot;, 10_000, &quot;BTC&quot;)
    bitcoin2: Coin = Coin(&quot;Bitcoin&quot;, 10_000, &quot;BTC&quot;)
    ripple: Coin = Coin(&quot;Ripple&quot;, 200, &quot;XRP&quot;)
    
    print(bitcoin1)
    print(ripple)
    
    print(f&quot;(bitcoin1 == ripple): {bitcoin1 == ripple}&quot;)
    print(f&quot;(bitcoin1 == bitcoin2): {(bitcoin1 == bitcoin2)}&quot;)
    

main()

# 결과
Coin(name='Bitcoin', value=10000, id='BTC')
Coin(name='Ripple', value=200, id='XRP')
(bitcoin1 == ripple): False
(bitcoin1 == bitcoin2): True&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1732835762819&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from dataclasses import dataclass, field

@dataclass
class Fruit:
    name: str
    grams: float
    price_per_kg: float
    edible: bool = True
    related_fruits: list[str] = field(default_factory=list)

def main():
    apple: Fruit = Fruit('apple', 100, 5)
    pear: Fruit = Fruit('Pear', 250, 10, related_fruits=['Apple', 'Orange'])
    print(apple)
    print(pear)
    print(pear.related_fruits)

main()

# 결과
Fruit(name='apple', grams=100, price_per_kg=5, edible=True, related_fruits=[])
Fruit(name='Pear', grams=250, price_per_kg=10, edible=True, related_fruits=['Apple', 'Orange'])
['Apple', 'Orange']&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Python</category>
      <category>Python</category>
      <author>techbard</author>
      <guid isPermaLink="true">https://techbard.tistory.com/3165</guid>
      <comments>https://techbard.tistory.com/3165#entry3165comment</comments>
      <pubDate>Sat, 17 May 2025 16:05:30 +0900</pubDate>
    </item>
    <item>
      <title>itertools</title>
      <link>https://techbard.tistory.com/3213</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1743420996573&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Sequence comparisons

import itertools

# define some lists
seq_1 = [1, 2, 3, 6, 10, 15, 34, 56]
seq_2 = [1, 2, 5, 7, 9, 18, 22, 38, 91]

# define a tuple
seq_3 = (1, 2, 3, 6, 10, 15, 34, 56)

# compare the sequences
# seq_1 vs seq_2
print(f&quot;Is seq_1 is equal to seq_2: {seq_1 == seq_2}&quot;) # two sequences are obviously different.
print(f&quot;Is seq_1 is greater than seq_2: {seq_1 &amp;gt; seq_2}&quot;) # third valus of seq_1 is not greater than seq_2.
print(f&quot;Is seq_1 is less than seq_2: {seq_1 &amp;lt; seq_2}&quot;) # necause 3 is less than the 5.

# output
Is seq_1 is equal to seq_2: False
Is seq_1 is greater than seq_2: False
Is seq_1 is less than seq_2: True


# sequences that have equal values but different number of items.
seq_4 = [10, 20, 30]
seq_5 = [10, 20, 30, 40, 50]
print(f&quot;Is seq_4 is less than seq_5: {seq_4 &amp;lt; seq_5}&quot;)

# output
Is seq_4 is less than seq_5: True

# Sequences must be of the same type to be compared
print(f&quot;Is the same type of seq_1 and seq_3: {seq_1 == seq_3}&quot;) # that's different types.
print(f&quot;Is the same type of seq_1 and seq_3: {tuple(seq_1) == seq_3}&quot;) # these are tuples.

# use the all() funtion to compare two arbitrary sequences
result = all(x == y for x, y in itertools.zip_longest(seq_1, seq_3))
print(result)

# output
Is the same type of seq_1 and seq_3: False
Is the same type of seq_1 and seq_3: True
True&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1743472294334&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import itertools

names = [&quot;Joe&quot;, &quot;Jane&quot;, &quot;Jim&quot;]

# cycle iterator can be used to cycle over a collection infinitely
cycler = itertools.cycle(names)
print(next(cycler))
print(next(cycler))
print(next(cycler))
print(next(cycler))

# output
Joe
Jane
Jim
Joe

#  use count to create a simple counter
counter = itertools.count(100, 10)
print(next(counter))
print(next(counter))
print(next(counter))

# output
100
110
120

# accumulate creates an iterator that accumulates values
vals = [10, 20, 30, 40, 50, 40, 30]
acc = itertools.accumulate(vals)
print(list(acc))

# output
[10, 30, 60, 100, 150, 190, 220] # it's running total.

# amortize a loan over a set number of payments for a 2000 loan at 4%
payments = [100, 125, 200, 105, 100, 120, 110, 130, 150, 100, 110, 120]
update = lambda balance, payment: round(balance * 1.04) - payment
balances = itertools.accumulate(payments, update, initial=2_000)
print(list(balances))

# output
[2000, 1980, 1934, 1811, 1778, 1749, 1699, 1657, 1593, 1507, 1467, 1416, 1353]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1743480129501&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Itertools: chain, chain.from_iterable

import itertools

# chain() creates a single iterable from multiple
chained = itertools.chain(&quot;abcd&quot;, &quot;1234&quot;)
print(list(chained))

# output
['a', 'b', 'c', 'd', '1', '2', '3', '4']

# Using chain() for build a flat list
list_1 = [[1, 2, 3, 4], [5]]
list_2 = [6, 7]

flat_list = itertools.chain(list_1, list_2)
print(list(flat_list))

# output
[[1, 2, 3, 4], [5], 6, 7]

flat_list = itertools.chain(list_1, list_2)
flat_int = [inner for outer in flat_list for inner in (outer if isinstance(outer, list) else [outer])] # flatten
print(flat_int)

# output
[1, 2, 3, 4, 5, 6, 7]


# chain.from_iterable is an alternate usage of chain
s1 = &quot;abcdefg&quot;
s2 = [1, 2, 3, 4, 5]
s3 = ['$', '%', '@', '&amp;amp;']

result = itertools.chain.from_iterable([s1, s2, s3])
print(list(result))

# output
['a', 'b', 'c', 'd', 'e', 'f', 'g', 1, 2, 3, 4, 5, '$', '%', '@', '&amp;amp;']&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1743483838295&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Itertools: dropwhile, takewhile, filterfalse
import itertools
import pprint
from dataclasses import dataclass

vals = [10, 20, 30, 40, 50, 40, 30, 25, 55, 45, 40, 30]

# dropwhile and takewhile will return values until
# a certain conditions is met that stops them.

def condition_func(n):
    return n &amp;lt; 40

# dropwhile() drops values until the predicate expression is True.
print(list(itertools.dropwhile(condition_func, vals)))

# takewhile() is the oppsite of dropwhile() - it returns values from
# the iterable while the predicate is True, then stops.
print(list(itertools.takewhile(condition_func, vals)))

# output
[40, 50, 40, 30, 25, 55, 45, 40, 30]
[10, 20, 30]


# filterfalse() returns elements from the iterable for which the predicate
# function returns False.
result = list(itertools.filterfalse(lambda x: x % 2 == 0, vals))
print(result)

# output
[25, 55, 45]

# These functions can work on complex objects.
@dataclass
class wcdata:
    game: str
    attendance: int
    team1: str
    team2: str
    score: str

world_cup_data = [
    wcdata(&quot;Final&quot;, 88966, &quot;Argentina&quot;, &quot;France&quot;, &quot;3 (4) -- 3 (2)&quot;),
    wcdata(&quot;3rd Place&quot;, 44137, &quot;Croatia&quot;, &quot;Moroco&quot;, &quot;2 -- 1&quot;),
    wcdata(&quot;Semi final&quot;, 68294, &quot;France&quot;, &quot;Moroco&quot;, &quot;2 -- 0&quot;),
    wcdata(&quot;Semi final&quot;, 88966, &quot;Argentina&quot;, &quot;Croatia&quot;, &quot;3 -- 0&quot;),
]

result = list(itertools.filterfalse(lambda x: x.attendance &amp;lt; 80_000, world_cup_data))
pprint.pp(result)

# output
[wcdata(game='Final',
        attendance=88966,
        team1='Argentina',
        team2='France',
        score='3 (4) -- 3 (2)'),
 wcdata(game='Semi final',
        attendance=88966,
        team1='Argentina',
        team2='Croatia',
        score='3 -- 0')]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1743488052391&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Itertools: combinations and permutatios

import itertools
import pprint

# product() produces the cartesian product of input iterables
cards = &quot;A23456789TJQK&quot; # cards of Ace to King.
suits = &quot;SCHD&quot;

deck = list(itertools.product(cards, suits))
print(len(deck), &quot;cards&quot;)
pprint.pp(deck)

# output
52 cards
[('A', 'S'),
 ('A', 'C'),
 ('A', 'H'),
 ('A', 'D'),
 ('2', 'S'),
 ('2', 'C'),
 ('2', 'H'),
 ('2', 'D'),
 ('3', 'S'),
 ('3', 'C'),
 ('3', 'H'),
 ('3', 'D'),
 ('4', 'S'),
 ('4', 'C'),
 ('4', 'H'),
 ('4', 'D'),
 ('5', 'S'),
 ('5', 'C'),
 ('5', 'H'),
 ('5', 'D'),
 ('6', 'S'),
 ('6', 'C'),
 ('6', 'H'),
 ('6', 'D'),
 ('7', 'S'),
 ('7', 'C'),
 ('7', 'H'),
 ('7', 'D'),
 ('8', 'S'),
 ('8', 'C'),
 ('8', 'H'),
 ('8', 'D'),
 ('9', 'S'),
 ('9', 'C'),
 ('9', 'H'),
 ('9', 'D'),
 ('T', 'S'),
 ('T', 'C'),
 ('T', 'H'),
 ('T', 'D'),
 ('J', 'S'),
 ('J', 'C'),
 ('J', 'H'),
 ('J', 'D'),
 ('Q', 'S'),
 ('Q', 'C'),
 ('Q', 'H'),
 ('Q', 'D'),
 ('K', 'S'),
 ('K', 'C'),
 ('K', 'H'),
 ('K', 'D')]

# permutations() creates tuples of a given length with no repeated elements
# 4개의 팀이 있고 각각 홈 - 원정 두 번씩 경기를 치른다. (each of the permutations to have two elements.)
# 총 경기 조합은?
teams = (&quot;A&quot;, &quot;B&quot;, &quot;C&quot;, &quot;D&quot;)
result = list(itertools.permutations(teams, 2))
print(result)

# output
[('A', 'B'), ('A', 'C'), ('A', 'D'), ('B', 'A'), ('B', 'C'), ('B', 'D'), ('C', 'A'), ('C', 'B'), ('C', 'D'), ('D', 'A'), ('D', 'B'), ('D', 'C')]

# combinations() will create combinations of a given length with no repeats
result = list(itertools.combinations(&quot;ABCD&quot;, 3)) # using combinations, the order doesn't matter.
# A, B, C and C, B, A would be considered the same, so it only appears once.
print(result)

# output
[('A', 'B', 'C'), ('A', 'B', 'D'), ('A', 'C', 'D'), ('B', 'C', 'D')]

# combinations_with_replacement() will create combinations of a given length with repeats.
result = list(itertools.combinations_with_replacement(&quot;ABCD&quot;, 3))
print(result)

# output
[('A', 'A', 'A'), ('A', 'A', 'B'), ('A', 'A', 'C'), ('A', 'A', 'D'), ('A', 'B', 'B'), ('A', 'B', 'C'), ('A', 'B', 'D'), ('A', 'C', 'C'), ('A', 'C', 'D'), ('A', 'D', 'D'), ('B', 'B', 'B'), ('B', 'B', 'C'), ('B', 'B', 'D'), ('B', 'C', 'C'), ('B', 'C', 'D'), ('B', 'D', 'D'), ('C', 'C', 'C'), ('C', 'C', 'D'), ('C', 'D', 'D'), ('D', 'D', 'D')]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1745659924354&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from itertools import product, permutations

for p in product((1, 2), (3, 4)):
    print(p) # Cartesian product

#output
(1, 3)
(1, 4)
(2, 3)
(2, 4)

for p in permutations([1, 2, 3]):
    print(p) # Order permutations
#output
(1, 2, 3)
(1, 3, 2)
(2, 1, 3)
(2, 3, 1)
(3, 1, 2)
(3, 2, 1)&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Python</category>
      <category>Python</category>
      <author>techbard</author>
      <guid isPermaLink="true">https://techbard.tistory.com/3213</guid>
      <comments>https://techbard.tistory.com/3213#entry3213comment</comments>
      <pubDate>Sat, 26 Apr 2025 18:32:49 +0900</pubDate>
    </item>
    <item>
      <title>list</title>
      <link>https://techbard.tistory.com/2945</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1729294571073&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;empty_list = [] # 빈 리스트를 만든다.
print(type(empty_list)) # 리스트인가?
# 결과
&amp;lt;class 'list'&amp;gt;

print(len(empty_list)) # 방금 생성한 리스트에는 아무것도 들어있지 않다.
# 결과
0

list_is_Container = []
list_is_Container[0:1] = '1' # 리스트의 첫째 자리에 1이라는 스트링을 넣는다.
print(list_is_Container)
# 결과
['1']

one_list = []
another_list = [2]
one_list.append(another_list) # 리스트 안에 또 다른 리스트를 넣는다.
print(one_list)
# 결과
[[2]]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1730785384390&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 리스트 요소의 삭제

planets = ['pluto', 'mars', 'earth', 'venus']
del planets[0] # 원본 리스트에서 삭제하는 방법 #1
print(planets)

print('')

planets = ['pluto', 'mars', 'earth', 'venus']
planets.remove('pluto') # 원본 리스트에서 삭제하는 방법 #2
print(planets)

# ==&amp;gt; del은 index 요소로 삭제 / remove 메서드는 요소 이름으로 삭제

# 결과
['mars', 'earth', 'venus']

['mars', 'earth', 'venus']&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;리스트의 할당과 복사&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1683026248478&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def main():

    l1 = [1, 2, 3]
    l2 = l1

    # 두 객체는 같은 객체이다.
    print('addr of id1: ', id(l1))
    print('addr of id2: ', id(l2))
    if id(l1) == id(l2):
        print('[l1 is l2] - same object')
    else:
        print('[l1 is not l2]')

    print('\t')

    one = list(range(1, 4))
    another = one.copy()

    # 두 객체는 다른 객체이다.
    print('addr of one: ', id(one))
    print('addr of another: ', id(another))
    if id(one) == id(another):
        print('[one is another]')
    else:
        print('[one is not another] - diff. object')

    pass

if __name__ == '__main__':
    main()

# 결과

addr of id1:  2531229270592
addr of id2:  2531229270592
[l1 is l2] - same object
	
addr of one:  2531230033536
addr of another:  2531229270208
[one is not another] - diff. object&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1730789007176&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# deep copy (cf. shallow copy)
# mutable 객체는 복사에 레퍼런스 참조 사용
# 객체를 직접 복사하려면 copy 모듈 사용
import copy

org_list = [1, 1, 1]
cpy_list = copy.deepcopy(org_list)

print(f'original list: {org_list}')
print(f'copied list: {cpy_list}')

cpy_list[0] = 0
print(f'still original list: {org_list}')
print(f'modify copied list: {cpy_list}')

# 결과
original list: [1, 1, 1]
copied list: [1, 1, 1]
still original list: [1, 1, 1]
modify copied list: [0, 1, 1]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731395740399&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 중첩 리스트의 인덱싱
nested_list = [[1, 2, 3], ['x', 'y'], ['apple', 'banana']]

print(&quot;before: &quot;, end='')
print(f'{nested_list}, length: {len(nested_list)}')

nested_list[0].append(4)
nested_list[1].append('z')
nested_list[2].append('cherry')

print(&quot;after: &quot;, end='')
print(f'{nested_list}, length: {len(nested_list)}')

# 각 내부 리스트에 요소를 추가했지만 내부 리스트 전체 개수의 변화는 없다.
before: [[1, 2, 3], ['x', 'y'], ['apple', 'banana']], length: 3
after: [[1, 2, 3, 4], ['x', 'y', 'z'], ['apple', 'banana', 'cherry']], length: 3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731396468643&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 리스트의 더블 인덱싱
in_list = [1, 2, 3]
l = []

for i in range(len(in_list)):
    l.append(in_list[i])

print(l)

print([1, 2, 3][0], end=' ')
print([1, 2, 3][1], end=' ')
print([1, 2, 3][2])

# 결과
[1, 2, 3]
1 2 3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731397090882&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 중첩 리스트의 변경
nested_list = [[1, 2, 3], ['x', 'y'], ['apple', 'banana']]

nested_list[1] = ['x', 'y', 'z']
nested_list[2][1] = 'cherry'

print(nested_list)

# 결과
[[1, 2, 3], ['x', 'y', 'z'], ['apple', 'cherry']]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731397715622&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 연속 중첩 리스트의 인덱싱
data_list = ['a', [1, 2, []], [['apple', 'banana'], 'cherry']]

fruits = data_list[2][0][0]

print(fruits)

# 결과
apple&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731413028746&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 중첩 리스트를 가지고 kv dict를 생성해 데이터 정리
# isinstance(x, (int, float, complex)) and not isinstance(x, bool)

key_data = ['nums', 'alphas', 'fruits']
nl_dict = { k : [] for k in key_data} # {'nums': [], 'alphas': [], 'fruits': []}

nested_list = [[1, 2, 3], ['x', 'y', 'z'], ['apple', 'cherry']]

for nl in nested_list:
    if isinstance(nl[0], int):
        nl_dict['nums'] = nl
    elif isinstance(nl[0], str) and len(nl[0]) == 1:
        nl_dict['alphas'] = nl
    else:
        nl_dict['fruits'] = nl

print(nl_dict)

# 결과
{'nums': [1, 2, 3], 'alphas': ['x', 'y', 'z'], 'fruits': ['apple', 'cherry']}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731481754633&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Nested Iteration
info = [['1', 10], ['2', 20], ['3', 30]]

values = []

for vl in info:
    values.append(vl[1])

print(values)

# 결과
[10, 20, 30]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731537961718&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Having this mixture is going to make it hard to traverse
# the Nested Data structure.
# (TypeError: 'int' object is not itrerable.)
#
# The solution when we are in control of the data structure
# is to make it very regular with always the same kinds of items
# in the same level of nesting.
# In other words, dont mix integers, lists and dictionaries as items
# in a single list.
#
# Sometimes however, we arent in control of the data structure.

mixture_of_nested_list = [1, 2, ['a', 'b'], ['c'], ['d', 'e', 'f']]

for e in mixture_of_nested_list:
    print(&quot;level1: &quot;)
    if type(e) is list:
        print(f&quot;\tlevel2: {e}&quot;)
    else:
        print(e)

# 결과
level1: 
1
level1: 
2
level1: 
	level2: ['a', 'b']
level1: 
	level2: ['c']
level1: 
	level2: ['d', 'e', 'f']&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1731542342001&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# shallow copy
original = [[1, 2], ['1', '2']]
copied1 = original
copied2 = original[:]

print(f&quot;Is copied1 equals original? {copied1 is original}&quot;)
print(f&quot;Is copied2 equals original? {copied2 is original}&quot;)
print(&quot;&quot;)

original.append([3])

print(f&quot;Now original is {original}&quot;)
print(f&quot;Now copied1 is {copied1}&quot;)
print(f&quot;Now copied2 is {copied2}&quot;)
print(&quot;&quot;)
# shallow copy (= copied2)는 추가 요소에 대해서는 구별되나
# 기존 요소는 copied1 = copied2 이다.

# 그러면 기존 요소를 바꾸는 경우 두 개가 달라질까?
# 먼저 리스트 요소 자체를 바꾸는 경우
del original[-1]
copied1[0] = ['x']

print(copied1)
print(copied2)
print(&quot;&quot;)
# ==&amp;gt; 정상이다.

# 그러면 리스트 내의 요소를 바꾸는 경우
copied1 = original
copied2 = original[:]

copied1[0][0] = 'x'
print(copied1)
print(copied2)
# =&amp;gt; 안된다. 리스트 요소를 바꾸는 경우 두 객체가 같아지는 문제가 있고, 이것이 shallow copy의 문제이다.

# 결과
Is copied1 equals original? True
Is copied2 equals original? False

Now original is [[1, 2], ['1', '2'], [3]]
Now copied1 is [[1, 2], ['1', '2'], [3]]
Now copied2 is [[1, 2], ['1', '2']]

[['x'], ['1', '2']]
[[1, 2], ['1', '2']]

[['x'], ['1', '2']]
[['x'], ['1', '2']]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1732171142249&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# list null check

empty_list = []

if not empty_list: # The pretty simple pythonic way.
    print(&quot;[1] null list&quot;)

if len(empty_list) == 0: # A much explicit way.
    print(&quot;[2] null list&quot;)

if empty_list == []: # Comparing it to an anonymous empty list.
    print(&quot;[3] null list&quot;)

if not bool(empty_list): # less readable surely
    print(&quot;[4] null list&quot;)

# 결과
[1] null list
[2] null list
[3] null list
[4] null list&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1732183039134&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Reverse the list

ll = [list(), [1], 100]
print(f&quot;original list: {ll}&quot;)

reversed_list_by_slice = ll[::-1]
print(f&quot;reversed_list_by_slice: {reversed_list_by_slice}&quot;)

ll.reverse()
print(f&quot;reversed_list_by_fx: {ll}&quot;)
# sort, sorted의 예를 보듯이
# 함수는 리턴을 하고, 메서드는 리턴을 하지 않고 해당 인스턴스를 변경한다.

# 결과
original list: [[], [1], 100]
reversed_list_by_slice: [100, [1], []]
reversed_list_by_fx: [100, [1], []]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1732184055009&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Confirm that the ojects are the same with the id function

list1 = [1, 2, 3]
list2 = list1

list3 = [1, 2, 3]

print(f&quot;id(list1) == id(list2): {id(list1) == id(list2)}&quot;)
print(f&quot;list1 is list2: {list1 is list2}&quot;)
print(f&quot;list1 == list3: {list1 == list3}&quot;, end=' ')
print(&quot;* list1 and list3 are different object, but have same values.&quot;)

# 결과
id(list1) == id(list2): True
list1 is list2: True
list1 == list3: True * list1 and list3 are different object, but have same values.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1732186252534&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;lst_idx = [1, 2, 3]
lst_idx_len = len(lst_idx)

idx = 0
for idx in range(len(lst_idx)):
    out = lst_idx.pop(0)
    print(f&quot;{out} is popped.&quot;)
    idx += 1
    # index로 돌며 무조건 1st를 pop하는 경우는 원하는대로 된다.

print(&quot;&quot;)

lst_in = [1, 2, 3]

for e in lst_in:
    out = lst_in.pop(0)
    print(f&quot;{out} is popped.&quot;)
    # 이건 stop이 변하기 때문에 원하는 결과가 나오지 않는다.

# 결과
1 is popped.
2 is popped.
3 is popped.

1 is popped.
2 is popped.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1732541330482&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 리스트 안의 int를 int형으로 변환하기

number: list[int] = [1]

# 리스트 내 int를 int형으로 변환 방법 1
for num in number:
    print(type(num), num)

# 리스트 내 int를 int형으로 변환 방법 2
int_result = int(''.join(map(str, number)))
print(type(int_result), int_result)

# 리스트 내 int를 int형으로 변환 방법 3
ints = int(''.join(str(n) for n in number))
print(type(ints), ints)

# 결과
&amp;lt;class 'int'&amp;gt; 1
&amp;lt;class 'int'&amp;gt; 1
&amp;lt;class 'int'&amp;gt; 1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1732573505995&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# If you're looping through a list, a good rule of thumb is to
# never modify that list while you're looping through it,
# because it will often lead to unwanted or unexpected behavior
# in your program.
# And in general, you will always want to use a temporary list
# if you decide to make any changes while you're looping through
# the original list, and then transfer the changes from that
# temporary list to the original.

# 간략히 말하면 list 중 요소를 제거하고 싶으면
# looping 중인 list를 변경(제거)하지 말고, 그것을 제외한 나머지를 새로운 list에
# 담는 방식으로 구현하자.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1732862181069&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# string 처리가 복잡할 때는 list로 변환해서
# 가공 후에 다시 텍스트로 바꾸는 방법도 쉽다.
from string import whitespace, punctuation

title: str = &quot;Section 5: Rewriting an Immutable String&quot;
title_list: list[str] = list(title)
colon_position: int = title_list.index(':')
del title_list[:colon_position+1]

for i in range(len(title_list)):
    if title_list[i] in whitespace+punctuation:
        title_list[i] = '_'

title: str = ''.join(title_list)
print(title)
title = 'prefix' + title
print(title)

# 결과
_Rewriting_an_Immutable_String
prefix_Rewriting_an_Immutable_String&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1732871816904&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 임의로 리스트 데이터를 가공하는 구현
person_marks: list[str, int] = \
[
    ['Bob', 75],
    ['Sally', 83],
    ['John', 91],
    ['Dave', 77]
]

marks_avg: int = sum([mark for (person, mark) in person_marks]) / len(person_marks)
print(&quot;=====&quot; * 5)
print(f&quot;marks avg: {marks_avg}&quot;)
print(&quot;=====&quot; * 5)
person_marks_evaluation: dict[str, list[int, float]] = \
{person: [mark, &quot;above avg or equal&quot; if mark &amp;gt;= marks_avg else &quot;below avg&quot;] for (person, mark) in person_marks}
for (person, markeval) in person_marks_evaluation.items():
    print(f&quot;{person:&amp;lt;5}, mark:{markeval[0]}, eval:{markeval[1]}&quot;)

# 결과
=========================
marks avg: 81.5
=========================
Bob  , mark:75, eval:below avg
Sally, mark:83, eval:above avg or equal
John , mark:91, eval:above avg or equal
Dave , mark:77, eval:below avg&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1732884756270&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# The valueof None is equivalent to &quot;null&quot; in other languages.
# The following values are falsy:
#   - None, 0, False, Empty sequence or mapping, Zero-length String

nums: list[int] = [1, 2, 3, 4, 5]
nums_stats: dict[str, int|float] = {'sum': 0, 'min': 0, 'max': 0, 'avg': 0, 'len': 0}

nums_sum: int = sum(nums)
nums_max: int = max(nums)
nums_min: int = min(nums)
nums_len: int = len(nums)
nums_avg: float = round(nums_sum/nums_len, 2)

nums_stats['avg'] = nums_avg
nums_stats['len'] = nums_len
nums_stats['max'] = nums_max
nums_stats['min'] = nums_min
nums_stats['sum'] = nums_sum

print(f&quot;{nums_stats=}&quot;)

# 결과
nums_stats={'sum': 15, 'min': 1, 'max': 5, 'avg': 3.0, 'len': 5}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1732887338862&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# find longest name (색다르게 구현한 버전)
names: list[str] = ['Cecilia', 'Lise', 'Marie']
names_sorted: list[str] = sorted(names, key=lambda name: len(name), reverse=True)
print(f&quot;longest name: {names_sorted[0]}, len: {len(names_sorted[0])}&quot;)

# 결과
longest name: Cecilia, len: 7&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1732947014988&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 랜덤으로 숫자와 알파벳을 생성해 zip으로 묶고
# dict를 조회하며 min, max, sum, avg 추출

import random
# 97 - 122, min, max, sum, avg

nums: list[int] = random.choices(range(100), k=10)
chrs: list[str] = list(map(chr, random.choices(range(97, 123), k=10)))
num_chr_pairs: dict[str, int] = {chr: num for chr, num in list(zip(chrs, nums))}

def get_avg(d: dict[str, int]) -&amp;gt; float:
    keys: list[str] = list(d.keys())
    total: int = sum([d[k]for k in keys])
    return round(total/len(keys), 2)

# 결과
{'p': 11, 'm': 57, 's': 86, 'u': 21, 'l': 99, 'd': 59}
min key: p, min value: 11
max key: l, max value: 99
sum: 333
avg: 55.5

def get_sum(d: dict[str, int]) -&amp;gt; int:
    keys: list[str] = list(d.keys())
    return sum([d[k]for k in keys])

def get_max(d: dict[str, int]) -&amp;gt; str:
    keys: list[str] = list(d.keys())
    max_key: str = keys[0]

    for k, v in d.items():
        if d[k] &amp;gt;= d[max_key]:
            max_key = k
    return max_key

def get_min(d: dict[str, int]) -&amp;gt; str:
    keys: list[str] = list(d.keys())
    min_key: str = keys[0]

    for k, v in d.items():
        if d[k] &amp;lt;= d[min_key]:
            min_key = k
    return min_key

print(num_chr_pairs)
min_key: str = get_min(num_chr_pairs)
print(f&quot;min key: {min_key}, min value: {num_chr_pairs[min_key]}&quot;)

max_key: str = get_max(num_chr_pairs)
print(f&quot;max key: {max_key}, max value: {num_chr_pairs[max_key]}&quot;)

sum_keys: str = get_sum(num_chr_pairs)
print(f&quot;sum: {sum_keys}&quot;)

avg_keys: str = get_avg(num_chr_pairs)
print(f&quot;avg: {avg_keys}&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1733030223008&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# To access a member of a sequence type, use []

text: str = &quot;hello&quot;
list_str: list[str] = ['Bob', 'Sally', 'John']

extract_first_two: slice = slice(None, 2)

print(text[extract_first_two])
print(list_str[extract_first_two])

# 결과
he
['Bob', 'Sally']&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1733120991661&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 조회하는 리스트에서 요소를 제거할 경우
# 조회 대상 자체가 변경되기 때문에 원하는 결과가 나오지 않는다.

# 회피 방법 #1
nums = [1, 2, 3, 4, 5]
pos = 0
result = []

while pos != len(nums):
    n = nums[pos]
    if n % 2 == 0:
        result.append(n)
    nums.remove(nums[0])

print(result)

# 회피 방법 #2 (제거하지 않고 index를 증가한다.)
nums = [1, 2, 3, 4, 5]
pos = 0
result = []

while pos &amp;lt; len(nums):
    if nums[pos] % 2 == 0:
        result.append(nums[pos])
    pos += 1

print(result)

# 결과
[2, 4]
[2, 4]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1733176680903&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Count the X's in a matrix

matrix = [[&quot;X&quot;, &quot;O&quot;, &quot;O&quot;], [&quot;O&quot;, &quot;X&quot;, &quot;O&quot;], [&quot;O&quot;, &quot;O&quot;, &quot;X&quot;]]
flat = [item for sublist in matrix for item in sublist]
count_of_x = flat.count(&quot;X&quot;)
print(f&quot;{count_of_x=}&quot;)

# 결과
count_of_x=3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1735163795844&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;data = [4, 5, 104, 105, 110, 120, 130, 130, 150,
        160, 170, 183, 185, 187, 188, 191, 350, 360]

min_valid = 100
max_valid = 200
result = [d for d in data if min_valid &amp;lt; d &amp;lt; max_valid]
print(result)

# 또 다른 방법: 인덱스를 얻어 원 데이터에서 삭제하는 방법
# 그러나, 이렇게 하면 안 된다. 의도한 결과가 나오지 않음
# 즉, target 데이터를 조회하며 제거할 때는 원본을 건드리지 말고
# 또 다른 리스트를 생성하는 것이 올바른 방법이다.

for idx, d in enumerate(data):
    if (d &amp;lt; min_valid) or (d &amp;gt; max_valid):
        del data[idx]
print(data)

# Output:
[104, 105, 110, 120, 130, 130, 150, 160, 170, 183, 185, 187, 188, 191]
[5, 104, 105, 110, 120, 130, 130, 150, 160, 170, 183, 185, 187, 188, 191, 360]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1735171175767&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Removing Items from a List Backwards (Safety Solution)
data = [104, 101, 4, 105, 308, 103, 5,
        107, 100, 306, 106, 102, 108]

min_valid = 100
max_valid = 200

print(&quot;Original:                    &quot;, end='')
print(data)

for idx in range(len(data) - 1, -1, -1):
    if (data[idx] &amp;lt; min_valid) or (data[idx] &amp;gt; max_valid):
        del data[idx]

print(&quot;Safety removing of list item:&quot;, end='')
print(data)

# Output:
Original:                    [104, 101, 4, 105, 308, 103, 5, 107, 100, 306, 106, 102, 108]
Safety removing of list item:[104, 101, 105, 103, 107, 100, 106, 102, 108]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1743492762813&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# another way of flatten list 

my_int_list = [[1, 2], [3, 4], [5, 6]]

# # 방법 1 - sum 함수
answer = sum(my_int_list, [])
print(answer)

my_str_list = [[&quot;apple&quot;, &quot;banana&quot;], [&quot;cherry&quot;, &quot;durian&quot;]]
flat_list = sum(my_str_list, [])
print(flat_list)

# # 방법 2 - itertools.chain.from_iterable
import itertools
result = list(itertools.chain.from_iterable(my_str_list))
print(result)

# output
[1, 2, 3, 4, 5, 6]
['apple', 'banana', 'cherry', 'durian']
['apple', 'banana', 'cherry', 'durian']&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1743510456175&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# flatten list method #1

my_list = [[&quot;apple&quot;, &quot;banana&quot;], [&quot;cherry&quot;], [&quot;durian&quot;, &quot;elderberry&quot;]]

flat_list_1 = sum(my_list, [])
print(flat_list_1)

# flatten list method #2
import itertools
flat_list_2 = itertools.chain.from_iterable(my_list)
print(list(flat_list_2))

# flatten list method #3
flat_list_3 = [inner for outer in my_list for inner in outer]
print(flat_list_3)

# output
['apple', 'banana', 'cherry', 'durian', 'elderberry']
['apple', 'banana', 'cherry', 'durian', 'elderberry']
['apple', 'banana', 'cherry', 'durian', 'elderberry']&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1743555514988&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# list 평탄화, 이종의 리스트를 한 개의 리스트로 변환

my_list = [[&quot;apple&quot;, &quot;banana&quot;], [&quot;cherry&quot;], &quot;durian&quot;]

### Using list conprehension
flat_list_using_comprehension = \
[inner for outer in my_list for inner in (outer if isinstance(outer, list) else [outer])]
print(flat_list_using_comprehension)

# output
['apple', 'banana', 'cherry', 'durian']

### Using sum(), 하지만 리스트가 요소가 모두 균일해야 한다.
for e in my_list:
    if isinstance(e, list):
        continue
    else:
        idx = my_list.index(e)
        my_list[idx] = [e]
flat_list_using_sum_func = sum(my_list, [])
print(flat_list_using_sum_func)

# output
['apple', 'banana', 'cherry', 'durian']

### Using for - in - list comprehension
### list index를 건드리는 건 위험할 수 있어서 별도의 리스트를 생성한다.
processed_list = []
for e in my_list:
    if isinstance(e, list):
        processed_list.extend(e)
    else:
        processed_list.append(e)
print(processed_list)

# output
['apple', 'banana', 'cherry', 'durian']&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1743594065287&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;two_d_list = [[&quot;apple&quot;, &quot;banana&quot;], [&quot;cherry&quot;], &quot;durian&quot;, &quot;citrus&quot;]

### Using for-in list comprehension for build flat_list
fruit_list = [inner for outer in two_d_list for inner in (outer if isinstance(outer, list) else [outer])]
print(fruit_list)

### Make fruit dict by first letter
group_alpha = {}
for fruit in fruit_list:
    key = fruit[0]
    (group_alpha.setdefault(key, [])).append(fruit)
print(group_alpha)

### output
['apple', 'banana', 'cherry', 'durian', 'citrus']
{'a': ['apple'], 'b': ['banana'], 'c': ['cherry', 'citrus'], 'd': ['durian']}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1743633201985&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# make matrix to flat-list
two_d_fruits = [[&quot;apple&quot;, &quot;banana&quot;], [&quot;cherry&quot;], &quot;durian&quot;, &quot;cranberry&quot;]

group_fruit = {}
fruits = [inner for outer in two_d_fruits for inner in (outer if isinstance(outer, list) else [outer])]

for fruit in fruits:
    key = fruit[0]
    (group_fruit.setdefault(key, [])).append(fruit)
# output
{'a': ['apple'], 'b': ['banana'], 'c': ['cherry', 'cranberry'], 'd': ['durian']}

# make fruits dict based on first letter and count them
for key in group_fruit:
    group_fruit[key].insert(0, f&quot;len: {len(group_fruit[key])}&quot;)

print(group_fruit)

# output
{'a': ['len: 1', 'apple'], 'b': ['len: 1', 'banana'], 'c': ['len: 2', 'cherry', 'cranberry'], 'd': ['len: 1', 'durian']}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1745464931559&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;##############################################################
# 리스트 요소 중 특정 요소만 추출하기
# :요소 타입이 다른 리스트를 string 변환하고 특정 요소만 제거
##############################################################
mixed_list = [1, None, '2', None, 3]
filtered_list = [m for m in mixed_list if m != None]
output = '/'.join(map(str, filtered_list))
print(f&quot;{mixed_list} -&amp;gt; {output}&quot;)

##output
[1, None, '2', None, 3] -&amp;gt; 1/2/3&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1745652218887&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#-------------------------------------------------------------------------------
# * 주의 *
#-------------------------------------------------------------------------------
# for-loop안에서 요소 삭제를 하면 index가 증가하면서 요소를 삭제하기 때문에
# 첫번째는 요소 1을 삭제하고 index는 0 -&amp;gt; 1로 증가
# 두번째 index가 1로 증가한 요소는 3이 된다.
# 이유는 index 0 -&amp;gt; 1로 증가한 뒤, 1 요소를 삭제했기 때문에
# index 1이 지금은 3이 된다.
# 다시 요소 3을 삭제한 뒤 index는 2로 증가하지만 총 4개 요소 중에
# 남은 요소는 3, 4 밖에 없으므로 index 2 요소는 존재하지 않는다.

values = [1, 2, 3, 4]
for v in values:
    values.remove(v)
print(values)
##output
[2, 4]
#-------------------------------------------------------------------------------&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1745658334106&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#-------------------------------------------------------------------------------
# 모든 리스트 요소를 지우려면 값을 일일이 꺼내서 for-loop 사용한다.
values = [1, 2, 3, 4]
for v in values[:]:
    values.remove(v)
print(values)
##output
[]
#-------------------------------------------------------------------------------&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1745659090971&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#-------------------------------------------------------------------------------
# 아래 코드 내용으로 볼 때 for-loop에서는 index를 자동으로 꺼내 사용한다.
# 왜냐하면 뒤에서부터 삭제하면 모든 요소 삭제가 정상이므로
# (앞에서 부터 index 삭제하면, 하나씩 밀리면서 원하는 삭제가 안 되고 남는다.)
values = [1, 2, 3, 4]
for v in reversed(values):
    values.remove(v)
print(values)
##output
[]
#-------------------------------------------------------------------------------&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Python</category>
      <category>Python</category>
      <author>techbard</author>
      <guid isPermaLink="true">https://techbard.tistory.com/2945</guid>
      <comments>https://techbard.tistory.com/2945#entry2945comment</comments>
      <pubDate>Sat, 26 Apr 2025 18:18:57 +0900</pubDate>
    </item>
    <item>
      <title>String Manipulation - example</title>
      <link>https://techbard.tistory.com/3211</link>
      <description>&lt;pre id=&quot;code_1742553133870&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# text 파일의 내용을 리버스
# 1. 단어 내용을 리버스
# 2. 단어 순서를 리버스

# reverse words in a text file
import os
# os.path.join은 운영체제에 관계 없이 경로를 만들어 준다.
file_path = os.path.join('2025-03-10', '5 - example.txt')

reversed_words_letter = []
reversed_words_sequence = []

with open(file_path, 'r') as file:
    text = file.read()
    words = text.split()

    for word in words:
        reversed_words_letter.append(word[::-1])
    reversed_words_sequence = words[::-1]
        
print(reversed_words_letter)
print(&quot;=====================================&quot;)
print(reversed_words_sequence)

# Output
['stseroF', 'era', 'lativ',
=====================================
['generations.', 'future', 'for', 'ecosystems',&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1743402104175&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Built-in string constants

# String processing is one of the more common tasks
# that today's programmers have to deal with.

import string

built-in constants for a variety of needs (pre-defined constants)
print(string.ascii_letters)
print(string.ascii_lowercase)
print(string.ascii_uppercase)
print(string.digits)
print(string.hexdigits)
print(string.punctuation)

# output
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
abcdefghijklmnopqrstuvwxyz
ABCDEFGHIJKLMNOPQRSTUVWXYZ
0123456789
0123456789abcdefABCDEF
!&quot;#$%&amp;amp;'()*+,-./:;&amp;lt;=&amp;gt;?@[\]^_`{|}~

# # Define a test string
# test_string = &quot;The quick brown fox jumps OVER the laze dog.&quot;

# use an iterator to see if a string contains any punctuation
if any(c in string.punctuation for c in test_string):
    print(&quot;The string contains punctuation.&quot;)
else:
    print(&quot;No punctuation found.&quot;)

# output
The string contains punctuation.

# generate a secure random password
alphabet = string.ascii_letters + string.digits + string.punctuation
import secrets
password = ''.join(secrets.choice(alphabet) for i in range(10))
print(password)

# output
%%o'?T%nw5

# Check the strength of a password
def check_password_strength(password):
    &quot;&quot;&quot; To keep things simple,
        we'll just assume that a password that
        has more than or equal to 10 letters,
        and has at least one character each of punctuation digits,
        and lower and uppercase letters is string. &quot;&quot;&quot;
    if (len(password) &amp;gt;= 10 and
        any(char in string.punctuation for char in password) and
        any(char in string.digits for char in password) and
        any(char in string.ascii_letters for char in password)):
        return f&quot;{password} is a string password.&quot;
    else:
        return f&quot;{password} is a weak password.&quot;

print(check_password_strength(&quot;MyTestPa$$123!&quot;))
print(check_password_strength(&quot;password&quot;))
print(check_password_strength(&quot;pa$$w0rd!&quot;))

# output
MyTestPa$$123! is a string password.
password is a weak password.
pa$$w0rd! is a weak password.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1743408214620&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;test_string = &quot;The quick brown fox jumps OVER the lazy dog.&quot;

# Using find() to find the first occurrence of a substring. (remind: it's case sensitive)
print(&quot;First occurrence of 'the':&quot;, test_string.find('the'))

# Using index() to find the first occurrence of a substring (raises ValueError if not)
print(&quot;First occurrence of 'the':&quot;, test_string.index('the'))
try:
    print(&quot;First occurrence of 'fax':&quot;, test_string.index('fax'))
except ValueError as e:
    print(e)

# The 'in' operator can be used for Boolean testing.
print(&quot;Is 'fox' present:&quot;, &quot;fox&quot; in test_string)

# Using rfind() to find the last occeuuence of a substring.
print(&quot;Last occurrence of 'the':&quot;, test_string.rfind('the'))

# Using rindex() to find the last occeuuence of a substring (raises ValueError if not)
print(&quot;Last occurrence of 'jump':&quot;, test_string.rindex('jump'))

# replace() function will find content in the string and replace it.
# Now, one of the things to keep in mind is that, in Python,
# strings are immutable.
result = test_string.replace(&quot;lazy&quot;, &quot;tired&quot;)
print(result)

# output
First occurrence of 'the': 31
First occurrence of 'the': 31
substring not found
Is 'fox' present: True
Last occurrence of 'the': 31
Last occurrence of 'jump': 20
The quick brown fox jumps OVER the tired dog.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1743410011543&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Manipulating string content

test_string = &quot;The quick brown fox jumps OVER the lazy dog.&quot;

# upper, lower, title
print(test_string.upper())
print(test_string.lower())
print(test_string.title())

# output
THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG.
the quick brown fox jumps over the lazy dog.
The Quick Brown Fox Jumps Over The Lazy Dog.

# strip, lstrip, rstrip
test_string2 = &quot;   This string has whitespace   &quot;

all_stripped_str = test_string2.strip()
print(test_string2.strip(), &quot;len:&quot;, len(all_stripped_str))

left_stripped_str = test_string2.lstrip()
print(test_string2.lstrip(), &quot;len:&quot;, len(left_stripped_str))

right_strriped_str = test_string2.rstrip()
print(test_string2.rstrip(), &quot;len:&quot;, len(right_strriped_str))

# output
This string has whitespace len: 26
This string has whitespace    len: 29
   This string has whitespace len: 29

# split creates a sequence from a single string
words = test_string.split()
print(words)

# join concatenates ab iterable into a single string
say_hello = [&quot;Hello&quot;, &quot;world&quot;, &quot;from&quot;, &quot;Python&quot;]
separator = &quot; &quot;
print(separator.join(say_hello))

# output
['The', 'quick', 'brown', 'fox', 'jumps', 'OVER', 'the', 'lazy', 'dog.']
Hello world from Python&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1743414964811&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Formatting output strings

# Basic formatting - center(), ljust(), rjust()
# Python took care of the calculations of how long the string was
# and where to center it, or left, or right justify it in each one.
# width = 40

print(&quot;center&quot;.center(width, '-'))
print(&quot;left&quot;.ljust(width, '-'))
print(&quot;right&quot;.rjust(width, '-'))

# output
-----------------center-----------------
left------------------------------------
-----------------------------------right

# Formatting strings with format specification codes
val_1 = 1234.5678
val_2 = 10987.65
val_3 = 12.99
val_4 = -280.7

print(f&quot;{val_1:.2f}&quot;)
print(f&quot;{val_2:.2f}&quot;)
print(f&quot;{val_3:.2f}&quot;)
print(f&quot;{val_4:.2f}&quot;)

# output
1234.57
10987.65
12.99
-280.70


# Use alignment and width and leading zeros
print(f&quot;{val_1:&amp;gt;10.2f}&quot;) # right align within 10 spaces
print(f&quot;{val_2:&amp;gt;10.2f}&quot;)
print(f&quot;{val_3:&amp;gt;10.2f}&quot;)
print(f&quot;{val_4:&amp;gt;10.2f}&quot;)

# output
   1234.57
  10987.65
     12.99
   -280.70

# Use a grouping option and +/- signs
print(f&quot;{val_1:&amp;gt;-10,.2f}&quot;)
print(f&quot;{val_2:&amp;gt;-10,.2f}&quot;)
print(f&quot;{val_3:&amp;gt;-10,.2f}&quot;)
print(f&quot;{val_4:&amp;gt;-10,.2f}&quot;)

# ouput
  1,234.57
 10,987.65
     12.99
   -280.70

# Insert a fill character
print(f&quot;{val_1:_&amp;gt;-10,.2f}&quot;)
print(f&quot;{val_2:_&amp;gt;-10,.2f}&quot;)
print(f&quot;{val_3:_&amp;gt;-10,.2f}&quot;)
print(f&quot;{val_4:_&amp;gt;-10,.2f}&quot;)

# output
__1,234.57
_10,987.65
_____12.99
___-280.70

# Create format specifiers dynamically
format_spec = &quot;{val:{width}.{precision}f}&quot;.format(val=val_2, width=10, precision=2)
print(format_spec)

# output
  10987.65&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1743415698435&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# String processing example

import string

def process_string(the_str, term):
    result = {
        &quot;Punctuation&quot;: 0,
        &quot;Whitespace&quot;: 0,
        &quot;Uppercase&quot;: 0,
        &quot;Lowercase&quot;: 0,
        &quot;Found&quot;: False,
        &quot;Index&quot;: -1
    }

    for char in the_str:
        if char in string.punctuation:
            result[&quot;Punctuation&quot;] += 1
        elif char in string.whitespace:
            result[&quot;Whitespace&quot;] += 1
        elif char in string.ascii_uppercase:
            result[&quot;Uppercase&quot;] += 1
        elif char in string.ascii_lowercase:
            result[&quot;Lowercase&quot;] += 1
    
    result[&quot;Index&quot;] = the_str.upper().find(term.upper())
    result[&quot;Found&quot;] = result[&quot;Index&quot;] &amp;gt;= 0

    return result

test_string = &quot;The quick, brows 'fox' jumps OVER the lazy dog.&quot;
search_term = &quot;Fox&quot;

result = process_string(test_string, search_term)
print(result)


# output
{'Punctuation': 4, 'Whitespace': 8, 'Uppercase': 5, 'Lowercase': 30, 'Found': True, 'Index': 18}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1745467523072&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 대소문자 카운트
text = 'Hello Mr. Rogers, how are you this fine Tuesday?'

def count_lower_upper(strings):
    upper_case, lower_case, other_case = (0, 0, 0)
    for t in text:
        if t.isupper():
            upper_case += 1
        elif t.islower():
            lower_case += 1
        elif t.isspace():
            continue
        else:
            other_case += 1

    return {&quot;lower_case&quot;: lower_case, &quot;upper_case&quot;: upper_case, &quot;other_case&quot;: other_case, &quot;original&quot;: strings}

print(count_lower_upper(text))
##output
{'lower_case': 33, 'upper_case': 4, 'other_case': 3, 'original': 'Hello Mr. Rogers, how are you this fine Tuesday?'}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>Python</category>
      <category>Python</category>
      <author>techbard</author>
      <guid isPermaLink="true">https://techbard.tistory.com/3211</guid>
      <comments>https://techbard.tistory.com/3211#entry3211comment</comments>
      <pubDate>Thu, 24 Apr 2025 13:06:06 +0900</pubDate>
    </item>
  </channel>
</rss>