+ {!runtimeInitializing && !runtimeReady && (
+
{
+ if (terminalInstanceRef.current && termReady) {
+ initRuntime();
+ hideCursor(terminalInstanceRef.current);
+ terminalInstanceRef.current.write(
+ chalk.dim.bold.italic(
+ "(初期化しています...しばらくお待ちください)"
+ )
+ );
+ terminalInstanceRef.current.focus();
+ }
+ }}
+ />
+ )}
+ {runtimeInitializing && (
+
+ )}
+
+
+ );
+}
diff --git a/app/terminal/terminal.tsx b/app/terminal/terminal.tsx
new file mode 100644
index 0000000..fc533c5
--- /dev/null
+++ b/app/terminal/terminal.tsx
@@ -0,0 +1,161 @@
+"use client";
+
+import { useEffect, useRef, useState } from "react";
+import { Terminal } from "@xterm/xterm";
+import { FitAddon } from "@xterm/addon-fit";
+import "@xterm/xterm/css/xterm.css";
+
+/**
+ * 文字列の幅を計算する。
+ * 厳密にやるなら @xterm/xterm/src/common/input/UnicodeV6.ts を使うとよさそう
+ * (しかしそれをimportしても動かなかった)
+ *
+ * とりあえず日本語と英数を最低限区別するでっち上げ実装にしている
+ *
+ */
+export function strWidth(str: string): number {
+ let len: number = 0;
+ for (const char of str) {
+ if (char.charCodeAt(0) < 0x2000) {
+ len += 1;
+ } else {
+ len += 2;
+ }
+ }
+ return len;
+}
+
+/**
+ * contentsがちょうど収まるターミナルの高さを計算する
+ */
+export function getRows(contents: string, cols: number): number {
+ return contents
+ .split("\n")
+ .reduce(
+ (rows, line) => rows + Math.max(1, Math.ceil(strWidth(line) / cols)),
+ 0
+ );
+}
+
+// なぜか term.clear(); が効かない場合がある... これは効く
+export function clearTerminal(term: Terminal) {
+ // term.clear();
+ term.write("\x1b[3J\x1b[2J\x1b[1;1H");
+}
+export function hideCursor(term: Terminal) {
+ term.write("\x1b[?25l");
+}
+export function showCursor(term: Terminal) {
+ term.write("\x1b[?25h");
+}
+
+interface TerminalProps {
+ getRows?: (cols: number) => number;
+ onReady?: () => void;
+}
+export function useTerminal(props: TerminalProps) {
+ const terminalRef = useRef
(null!);
+ const terminalInstanceRef = useRef(null);
+ const fitAddonRef = useRef(null);
+ const [termReady, setTermReady] = useState(false);
+
+ const getRowsRef = useRef<(cols: number) => number>(undefined);
+ getRowsRef.current = props.getRows;
+ const onReadyRef = useRef<() => void>(undefined);
+ onReadyRef.current = props.onReady;
+
+ // ターミナルの初期化処理
+ useEffect(() => {
+ const abortController = new AbortController();
+
+ (async () => {
+ // globals.cssでフォントを指定し読み込んでいるが、
+ // それが読み込まれる前にterminalを初期化してしまうとバグる。
+ // なのでここでフォントをfetchし成功するまでterminalの初期化は待つ
+ try {
+ await fetch(
+ "https://cdn.jsdelivr.net/fontsource/fonts/inconsolata:vf@latest/latin-wght-normal.woff2",
+ { signal: abortController.signal }
+ );
+ } catch {
+ // ignore
+ }
+
+ if (!abortController.signal.aborted) {
+ const fromCSS = (varName: string) =>
+ window.getComputedStyle(document.body).getPropertyValue(varName);
+ // "--color-" + color_name のように文字列を分割するとTailwindCSSが認識せずCSSの値として出力されない場合があるので注意
+ const term = new Terminal({
+ cursorBlink: true,
+ convertEol: true,
+ cursorStyle: "bar",
+ cursorInactiveStyle: "none",
+ fontSize: 14,
+ lineHeight: 1.4,
+ letterSpacing: 0,
+ fontFamily: "Inconsolata Variable",
+ theme: {
+ // DaisyUIの変数を使用してテーマを設定している
+ // TODO: ダークテーマ/ライトテーマを切り替えたときに再設定する?
+ background: fromCSS("--color-base-300"),
+ foreground: fromCSS("--color-base-content"),
+ cursor: fromCSS("--color-base-content"),
+ selectionBackground: fromCSS("--color-primary"),
+ selectionForeground: fromCSS("--color-primary-content"),
+ black: fromCSS("--color-black"),
+ brightBlack: fromCSS("--color-neutral-500"),
+ red: fromCSS("--color-red-600"),
+ brightRed: fromCSS("--color-red-400"),
+ green: fromCSS("--color-green-600"),
+ brightGreen: fromCSS("--color-green-400"),
+ yellow: fromCSS("--color-yellow-700"),
+ brightYellow: fromCSS("--color-yellow-400"),
+ blue: fromCSS("--color-indigo-600"),
+ brightBlue: fromCSS("--color-indigo-400"),
+ magenta: fromCSS("--color-fuchsia-600"),
+ brightMagenta: fromCSS("--color-fuchsia-400"),
+ cyan: fromCSS("--color-cyan-600"),
+ brightCyan: fromCSS("--color-cyan-400"),
+ white: fromCSS("--color-base-100"),
+ brightWhite: fromCSS("--color-white"),
+ },
+ });
+ terminalInstanceRef.current = term;
+
+ fitAddonRef.current = new FitAddon();
+ term.loadAddon(fitAddonRef.current);
+ // fitAddon.fit();
+
+ term.open(terminalRef.current);
+
+ setTermReady(true);
+ onReadyRef.current?.();
+ }
+ })();
+
+ const observer = new ResizeObserver(() => {
+ // fitAddon.fit();
+ const dims = fitAddonRef.current?.proposeDimensions();
+ if (dims && !isNaN(dims.cols)) {
+ const rows = Math.max(5, getRowsRef.current?.(dims.cols) ?? 0);
+ terminalInstanceRef.current?.resize(dims.cols, rows);
+ }
+ });
+ observer.observe(terminalRef.current);
+
+ return () => {
+ abortController.abort("terminal component dismount");
+ observer.disconnect();
+ if (fitAddonRef.current) {
+ fitAddonRef.current.dispose();
+ fitAddonRef.current = null;
+ }
+ if (terminalInstanceRef.current) {
+ terminalInstanceRef.current.dispose();
+ terminalInstanceRef.current = null;
+ }
+ };
+ }, []);
+
+ return { terminalRef, terminalInstanceRef, termReady };
+}
diff --git a/next.config.ts b/next.config.ts
index a73bb20..8757724 100644
--- a/next.config.ts
+++ b/next.config.ts
@@ -1,5 +1,6 @@
import type { NextConfig } from "next";
import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare";
+import { version as pyodideVersion } from "pyodide";
initOpenNextCloudflareForDev();
@@ -11,6 +12,9 @@ const nextConfig: NextConfig = {
typescript: {
ignoreBuildErrors: true,
},
+ env: {
+ PYODIDE_VERSION: pyodideVersion,
+ },
};
export default nextConfig;
diff --git a/package-lock.json b/package-lock.json
index 71cb69a..b34b94e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,8 +10,17 @@
"dependencies": {
"@google/generative-ai": "^0.24.1",
"@opennextjs/cloudflare": "^1.6.3",
+ "@xterm/addon-fit": "^0.11.0-beta.115",
+ "@xterm/xterm": "^5.6.0-beta.115",
+ "ace-builds": "^1.43.2",
+ "async-mutex": "^0.5.0",
+ "chalk": "^5.5.0",
+ "clsx": "^2.1.1",
"next": "<15.4",
+ "prismjs": "^1.30.0",
+ "pyodide": "^0.28.1",
"react": "19.1.0",
+ "react-ace": "^14.0.1",
"react-dom": "19.1.0",
"react-markdown": "^10.1.0",
"react-syntax-highlighter": "^15.6.1",
@@ -22,6 +31,7 @@
"@eslint/eslintrc": "^3",
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
+ "@types/prismjs": "^1.26.5",
"@types/react": "^19",
"@types/react-dom": "^19",
"@types/react-syntax-highlighter": "^15.5.13",
@@ -9396,18 +9406,6 @@
"open-next": "dist/index.js"
}
},
- "node_modules/@opennextjs/aws/node_modules/chalk": {
- "version": "5.4.1",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
- "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
- "license": "MIT",
- "engines": {
- "node": "^12.17.0 || ^14.13 || >=16.0.0"
- },
- "funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
- }
- },
"node_modules/@opennextjs/cloudflare": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/@opennextjs/cloudflare/-/cloudflare-1.6.3.tgz",
@@ -11280,6 +11278,13 @@
"form-data": "^4.0.4"
}
},
+ "node_modules/@types/prismjs": {
+ "version": "1.26.5",
+ "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.5.tgz",
+ "integrity": "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/react": {
"version": "19.1.9",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.9.tgz",
@@ -11884,6 +11889,21 @@
"win32"
]
},
+ "node_modules/@xterm/addon-fit": {
+ "version": "0.11.0-beta.115",
+ "resolved": "https://registry.npmjs.org/@xterm/addon-fit/-/addon-fit-0.11.0-beta.115.tgz",
+ "integrity": "sha512-L9o6SHQdY6gOapiwFhg5HbeVQHskq3dgDX3OECX+SUKCHjKZDFgN/pgfngdMyK0LxKDzJu3vl5FnyrEtpyuyTg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@xterm/xterm": "^5.6.0-beta.115"
+ }
+ },
+ "node_modules/@xterm/xterm": {
+ "version": "5.6.0-beta.115",
+ "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.6.0-beta.115.tgz",
+ "integrity": "sha512-EJXAW6dbxPuwQnLfTmPB5R3M5uu8qp24ltHdjCcfwGpudKxQRoDEbq1IeGrVLIuRc/8TbnT1U07dXUX7kyGYEQ==",
+ "license": "MIT"
+ },
"node_modules/abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
@@ -11909,6 +11929,12 @@
"node": ">= 0.6"
}
},
+ "node_modules/ace-builds": {
+ "version": "1.43.2",
+ "resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.43.2.tgz",
+ "integrity": "sha512-3wzJUJX0RpMc03jo0V8Q3bSb/cKPnS7Nqqw8fVHsCCHweKMiTIxT3fP46EhjmVy6MCuxwP801ere+RW245phGw==",
+ "license": "BSD-3-Clause"
+ },
"node_modules/acorn": {
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
@@ -12196,6 +12222,15 @@
"node": ">= 0.4"
}
},
+ "node_modules/async-mutex": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz",
+ "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -12446,17 +12481,12 @@
}
},
"node_modules/chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "dev": true,
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.5.0.tgz",
+ "integrity": "sha512-1tm8DTaJhPBG3bIkVeZt1iZM9GfSX2lzOeDVZH9R9ffRHpmHvxZ/QhgQH/aDTkswQVt+YHdXAdS/In/30OjCbg==",
"license": "MIT",
- "dependencies": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- },
"engines": {
- "node": ">=10"
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
@@ -12641,6 +12671,15 @@
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"license": "MIT"
},
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/color": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
@@ -12978,6 +13017,12 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/diff-match-patch": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz",
+ "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==",
+ "license": "Apache-2.0"
+ },
"node_modules/doctrine": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
@@ -13706,6 +13751,23 @@
"url": "https://opencollective.com/eslint"
}
},
+ "node_modules/eslint/node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
"node_modules/espree": {
"version": "10.4.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
@@ -15411,7 +15473,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
- "dev": true,
"license": "MIT"
},
"node_modules/js-yaml": {
@@ -15785,6 +15846,20 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/lodash.get": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
+ "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==",
+ "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isequal": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+ "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
+ "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.",
+ "license": "MIT"
+ },
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@@ -15806,7 +15881,6 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
- "dev": true,
"license": "MIT",
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
@@ -17525,7 +17599,6 @@
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -17972,7 +18045,6 @@
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
- "dev": true,
"license": "MIT",
"dependencies": {
"loose-envify": "^1.4.0",
@@ -18013,6 +18085,18 @@
"node": ">=6"
}
},
+ "node_modules/pyodide": {
+ "version": "0.28.1",
+ "resolved": "https://registry.npmjs.org/pyodide/-/pyodide-0.28.1.tgz",
+ "integrity": "sha512-7O1jZdfUc4/9PAKzEIyLOh3yhxknTWA8xQaCfZ4R56pOnchS909x2sqt2Wh+qHf+b7MzyB8igE5ZzYdP1pZN5w==",
+ "license": "MPL-2.0",
+ "dependencies": {
+ "ws": "^8.5.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
"node_modules/qs": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
@@ -18082,6 +18166,23 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-ace": {
+ "version": "14.0.1",
+ "resolved": "https://registry.npmjs.org/react-ace/-/react-ace-14.0.1.tgz",
+ "integrity": "sha512-z6YAZ20PNf/FqmYEic//G/UK6uw0rn21g58ASgHJHl9rfE4nITQLqthr9rHMVQK4ezwohJbp2dGrZpkq979PYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ace-builds": "^1.36.3",
+ "diff-match-patch": "^1.0.5",
+ "lodash.get": "^4.4.2",
+ "lodash.isequal": "^4.5.0",
+ "prop-types": "^15.8.1"
+ },
+ "peerDependencies": {
+ "react": "^0.13.0 || ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^0.13.0 || ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/react-dom": {
"version": "19.1.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
@@ -18098,7 +18199,6 @@
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
- "dev": true,
"license": "MIT"
},
"node_modules/react-markdown": {
diff --git a/package.json b/package.json
index 9028127..6ad4485 100644
--- a/package.json
+++ b/package.json
@@ -16,8 +16,17 @@
"dependencies": {
"@google/generative-ai": "^0.24.1",
"@opennextjs/cloudflare": "^1.6.3",
+ "@xterm/addon-fit": "^0.11.0-beta.115",
+ "@xterm/xterm": "^5.6.0-beta.115",
+ "ace-builds": "^1.43.2",
+ "async-mutex": "^0.5.0",
+ "chalk": "^5.5.0",
+ "clsx": "^2.1.1",
"next": "<15.4",
+ "prismjs": "^1.30.0",
+ "pyodide": "^0.28.1",
"react": "19.1.0",
+ "react-ace": "^14.0.1",
"react-dom": "19.1.0",
"react-markdown": "^10.1.0",
"react-syntax-highlighter": "^15.6.1",
@@ -28,6 +37,7 @@
"@eslint/eslintrc": "^3",
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
+ "@types/prismjs": "^1.26.5",
"@types/react": "^19",
"@types/react-dom": "^19",
"@types/react-syntax-highlighter": "^15.5.13",
diff --git a/public/docs/python-1.md b/public/docs/python-1.md
index 967942b..ee67ed4 100644
--- a/public/docs/python-1.md
+++ b/public/docs/python-1.md
@@ -1,85 +1,111 @@
-# 第1章: Pythonへようこそ:環境構築と基本思想
+# 第1章: Pythonへようこそ:環境構築と基本
-プログラミング経験者にとって、新しい言語を学ぶ上で最も重要なことの一つは、その言語特有の「流儀」や設計思想を理解することです。この章では、Pythonの開発環境を構築し、Pythonがどのような考え方で作られているのかを探ります。
+プログラミング経験者であっても、言語ごとのツールや流儀を最初に理解することは重要です。この章では、Pythonの開発環境を整え、基本的なツールの使い方を学びます。
-## Pythonのインストールとバージョン管理
+## Pythonのインストール方法
-Pythonを始めるには、まずお使いのコンピュータにPythonをインストールする必要があります。しかし、プロジェクトごとに異なるバージョンのPythonを使いたい場面は頻繁にあります。そこで、複数のPythonバージョンを簡単に切り替えて管理できる **`pyenv`** の利用を強く推奨します。
+手元の環境で本格的に開発を進めるために、Pythonのインストール方法を紹介します。
-**`pyenv` とは?**
-`pyenv` は、システム全体に影響を与えることなく、ユーザーのホームディレクトリ内で複数のPythonバージョンを管理できるツールです。これにより、「プロジェクトAではPython 3.9を、プロジェクトBではPython 3.11を使う」といったことが容易になります。
+### Windows
-### インストール手順(macOS/Linuxの例):
-Homebrew(macOS)やgitを使って簡単にインストールできます。
+WindowsでPythonをインストールするには、主に2つの方法があります。
-1. **pyenvのインストール:**
+1. **[Python公式インストーラ](https://www.python.org/downloads/)**: Pythonの公式サイトからインストーラをダウンロードする方法が最も一般的です。インストール時に「Add Python to PATH」のチェックを入れると、コマンドプロンプトやPowerShellから `python` コマンドを直接実行できるようになり便利です。
+2. **Microsoft Store**: Microsoft Storeからも手軽にPythonをインストールできます。
- ```bash
- # Homebrewの場合 (macOS)
- brew install pyenv
- ```
+### macOS / Linux
-2. **シェルの設定:**
- インストール後、以下のコマンドをシェルの設定ファイル(`.zshrc`, `.bash_profile`など)に追加します。
+macOSでは、**Homebrew** というパッケージマネージャを使ってインストールするのが簡単です。
+`brew install python`
- ```bash
- eval "$(pyenv init --path)"
- eval "$(pyenv init -)"
- ```
+もちろん、Windowsと同様に公式サイトからインストーラをダウンロードすることも可能です。多くのLinuxディストリビューションには初めからPythonがインストールされていますが、最新版を使いたい場合はディストリビューションのパッケージマネージャ(`apt`, `yum`など)を利用するのが一般的です。
-3. **Pythonのインストール:**
- 利用可能なバージョンを確認し、特定のバージョンをインストールします。
+### バージョン管理と環境管理ツール
- ```bash
- # インストール可能なバージョンの一覧を表示
- pyenv install --list
+より高度な開発や、複数のプロジェクトを並行して進める場合は、バージョン管理ツールや統合的な環境管理ツールの利用が推奨されます。
- # 例として Python 3.11.5 をインストール
- pyenv install 3.11.5
- ```
+ * **[pyenv](https://github.com/pyenv/pyenv)**: 複数のPythonバージョン(例: 3.9と3.11)を一つのPCに共存させ、プロジェクトごとに切り替えるためのツールです。
+ * **[Conda](https://docs.conda.io/en/latest/)**: 特にデータサイエンスの分野で人気のあるツールです。**Conda** はPythonのバージョン管理だけでなく、パッケージ管理、仮想環境の管理までを一つでこなせるオールインワンのソリューションです。
-4. **バージョンの切り替え:**
- `pyenv` を使うと、ディレクトリごと、またはグローバルに使用するPythonのバージョンを切り替えられます。
+## 対話モード(REPL)でPythonを体験しよう
- ```bash
- # このディレクトリでは 3.11.5 を使う
- pyenv local 3.11.5
-
- # グローバルで 3.11.5 を使う
- pyenv global 3.11.5
- ```
-
-Windowsユーザーの方は、`pyenv`のWindows版である **`pyenv-win`** を利用すると同様の環境を構築できます。
+**REPL**(Read-Eval-Print Loop)は、入力したコードをその場で実行し、結果をすぐに見ることができる強力な学習・デバッグツールです。
+### ブラウザで今すぐ試す
-## 対話モード(REPL)の活用
+このウェブサイトではドキュメント内に Python {process.env.PYODIDE_PYTHON_VERSION} の実行環境を埋め込んでいます。
+以下のように青枠で囲われたコード例には自由にPythonコードを書いて試すことができます。気軽に利用してください。
-Pythonには **REPL** (Read-Eval-Print Loop) と呼ばれる対話モードが備わっています。これは、コードを1行書くたびに即座に実行・評価し、結果を返してくれる機能です。他の言語での経験者にとっても、新しいライブラリの動作確認や、ちょっとした文法のテストに非常に便利です。
-
-ターミナルで `python` と入力するだけで起動します。
-
-```python
-$ python
-Python 3.11.5 (main, Aug 24 2023, 15:09:47) [Clang 14.0.3 (clang-1403.0.22.14.1)] on darwin
-Type "help", "copyright", "credits" or "license" for more information.
+```python-repl
>>> message = "Hello, Python!"
>>> print(message)
Hello, Python!
>>> 1 + 2 * 3
7
->>> exit()
```
-より高機能なREPLとして **IPython** や **Jupyter Notebook** も人気があります。これらはコード補完や履歴管理、インラインでのグラフ描画など、さらに強力な機能を備えています。
+### 自分のPCで使う
+
+インストールが完了したら、自分のPCのターミナル(コマンドプロンプトやPowerShellなど)で `python` と入力すれば、同じ対話モードを起動できます。
+
+```
+$ python
+Python 3.11.5 (...)
+Type "help", "copyright", "credits" or "license" for more information.
+>>>
+```
+`>>>` というプロンプトが表示されたら準備完了です。
+
+### REPL の基本的な使い方
+
+* **計算:** 数式を直接入力すると、計算結果が返ってきます。
+```python-repl
+>>> 10 * 5 + 3
+53
+```
+* **変数と関数の利用:** 変数を定義したり、`print()`のような組み込み関数を呼び出したりできます。
+```python-repl
+>>> greeting = "Hi there"
+>>> print(greeting)
+Hi there
+```
+* **ヘルプ機能:** `help()` と入力するとヘルプが表示されます。調べたいモジュールや関数名(例: `str`)を入力するとドキュメントが表示されます。
+ * PCのターミナルで起動したREPLでは、対話的なヘルプモードが起動します。ヘルプモードを抜けるには `quit` と入力します。
+```python-repl
+>>> help(str)
+Help on class str in module builtins:
+
+class str(object)
+ | str(object='') -> str
+ | str(bytes_or_buffer[, encoding[, errors]]) -> str
+ | ...
+```
+* **終了方法:** REPLを終了するには、`exit()` と入力するか、ショートカットキー(macOS/Linuxでは `Ctrl + D`、Windowsでは `Ctrl + Z` を押してからEnter)を使用します。
+ * このウェブサイトに埋め込まれているREPLは、終了できません。
## スクリプトの実行方法
一連の処理をまとめて実行する場合は、`.py` という拡張子を持つファイルにコードを記述します。例えば、`hello.py` というファイルを以下のように作成します。
+REPLでは式を入力するだけでも結果が表示されていましたが、スクリプトで結果を表示するには `print()` 関数を使う必要があります。
-**hello.py**
+```python:hello.py
+print("Hello from a Python script!")
+```
-```python
+このスクリプトを実行するには、ターミナルで `python hello.py` のようにコマンドを入力します。
+
+このウェブサイト上では以下のように実行ボタンをクリックするとスクリプトの実行結果が表示されます。上の hello1.py のコードを変更して再度実行すると結果も変わるはずです。試してみてください。
+
+```python-exec:hello.py
+Hello from a Python script!
+```
+
+### __main__ について
+
+前述の hello.py のようにファイルの1行目から処理を書いても問題なく動作しますが、一般的には以下のようなお決まりの書き方が用いられます。
+
+```python:hello2.py
def main():
print("Hello from a Python script!")
@@ -87,87 +113,103 @@ if __name__ == "__main__":
main()
```
-`if __name__ == "__main__":` は、このスクリプトが直接実行された場合にのみ `main()` 関数を呼び出すためのお決まりの書き方です。他のスクリプトからモジュールとしてインポートされた際には `main()` は実行されません。
+```python-exec:hello2.py
+Hello from a Python script!
+```
-このスクリプトを実行するには、ターミナルで以下のようにコマンドを入力します。
+なぜわざわざ `if __name__ == "__main__":` を使うのでしょうか?
+それは、**書いたコードを「スクリプトとして直接実行する」場合と、「他のファイルから部品(モジュール)として読み込んで使う」場合の両方に対応できるようにするため**です。
-```bash
-python hello.py
-```
+Pythonでは、ファイルは他のファイルから `import` 文で読み込むことができます。このとき、読み込まれたファイル(モジュール)は上から順に実行されます。
-出力結果:
+`if __name__ == "__main__":` を使うと、**「このファイルがコマンドラインから直接 `python a.py` のように実行された時だけ、このブロックの中の処理を実行してね」** という意味になります。
-```
-Hello from a Python script!
+**例:再利用可能な関数を持つスクリプト**
+
+```python:my_utils.py
+def say_hello(name):
+ """挨拶を返す関数"""
+ return f"Hello, {name}!"
+
+# このファイルが直接実行された時だけ、以下のテストコードを実行する
+if __name__ == "__main__":
+ print("--- Running Test ---")
+ message = say_hello("Alice")
+ print(message)
+ print("--- Test Finished ---")
```
+このファイルを2通りの方法で使ってみます。
-## Pythonの禅 (The Zen of Python)
+1. **直接スクリプトとして実行する**
-Pythonには、その設計哲学を端的に表した **「The Zen of Python」** という短い詩のような文章があります。これはPythonの思想を理解する上で非常に重要です。対話モードで `import this` と入力すると表示されます。
+ ```python-exec:my_utils.py
+ --- Running Test ---
+ Hello, Alice!
+ --- Test Finished ---
+ ```
-```python
->>> import this
-The Zen of Python, by Tim Peters
+2. **他のファイルからモジュールとして読み込む**
-Beautiful is better than ugly.
-Explicit is better than implicit.
-Simple is better than complex.
-Complex is better than complicated.
-Flat is better than nested.
-Sparse is better than dense.
-Readability counts.
-...
-```
+ ```python:main_app.py
+ # my_utils.py から say_hello 関数だけを読み込む
+ from my_utils import say_hello
-「美しいは醜いより良い」「明示的は暗黙的より良い」「シンプルは複雑より良い」といった言葉は、Pythonでコードを書く上での指針となります。Pythonらしいコードとは、**読みやすく、シンプルで、意図が明確なコード**であると言えます。
+ print("--- Running Main App ---")
+ greeting = say_hello("Bob")
+ print(greeting)
+ ```
-## パッケージ管理ツール `pip` と仮想環境 `venv`
+ ```python-exec:main_app.py
+ --- Running Main App ---
+ Hello, Bob!
+ ```
-Pythonの強力なエコシステムは、豊富なサードパーティ製パッケージ(ライブラリ)によって支えられています。これらのパッケージを管理するのが **`pip`** です。
+ `my_utils.py` のテストコード(`--- Running Test ---`など)は実行されず、`say_hello` 関数だけを部品として利用できました。
-**`pip`** はPythonの標準パッケージインストーラで、例えばデータ分析で人気の `pandas` をインストールするには、以下のコマンドを実行します。
+このように、`if __name__ == "__main__":` は、**再利用可能な関数やクラスの定義**と、**そのファイル単体で動かすための処理**をきれいに分離するための、Pythonにおける非常に重要な作法です。
-```bash
-pip install pandas
-```
+## パッケージ管理ツール `pip` と仮想環境 `venv`
+
+Pythonの強力なエコシステムは、豊富なサードパーティ製パッケージ(ライブラリ)によって支えられています。これらのパッケージを管理するのが **`pip`** です。
-しかし、プロジェクトAでは `pandas` のバージョン1.0が必要で、プロジェクトBでは2.0が必要、といった依存関係の衝突が起こる可能性があります。これを解決するのが **仮想環境** です。
+しかし、プロジェクトごとに異なるバージョンのパッケージを使いたい場合、依存関係の衝突が問題になります。これを解決するのが **仮想環境** で、Pythonでは **`venv`** モジュールを使って作成するのが標準的です。
-**`venv`** は、プロジェクトごとに独立したPython環境を作成するための標準モジュールです。仮想環境を有効にすると、`pip` でインストールしたパッケージはその環境内にのみ保存され、他のプロジェクトやシステムのPython環境を汚染しません。
+**仮想環境とは?** 🚧
+プロジェクト専用の独立したPython実行環境です。ここでインストールしたパッケージはシステム全体には影響を与えず、そのプロジェクト内に限定されます。
-**仮想環境の作成と利用:**
+**基本的な流れ:**
-1. **作成:** プロジェクトディレクトリで以下のコマンドを実行します。(`.venv` は仮想環境を保存するディレクトリ名で、慣例的によく使われます)
+1. **仮想環境の作成**:
```bash
+ # .venvという名前の仮想環境を作成
python -m venv .venv
```
-2. **有効化 (Activate):**
+2. **仮想環境の有効化(Activate)**:
```bash
# macOS / Linux
source .venv/bin/activate
- # Windows (Command Prompt)
- .\.venv\Scripts\activate
+ # Windows (PowerShell)
+ .\.venv\Scripts\Activate.ps1
```
- 有効化すると、プロンプトの先頭に `(.venv)` のような表示が追加され、このターミナルセッションでは仮想環境が使われていることが分かります。
+ 有効化すると、ターミナルのプロンプトに `(.venv)` のような表示が付きます。
-3. **パッケージのインストール:**
- この状態で `pip install` を実行すると、パッケージは `.venv` ディレクトリ内にインストールされます。
+3. **パッケージのインストール**:
+ 有効化された環境で `pip` を使ってパッケージをインストールします。
```bash
(.venv) $ pip install requests
```
-4. **無効化 (Deactivate):**
- 仮想環境から抜けるには `deactivate` コマンドを使います。
+4. **仮想環境の無効化(Deactivate)**:
```bash
(.venv) $ deactivate
```
-`pyenv` でPythonのバージョンを管理し、`venv` でプロジェクトごとのパッケージを管理する。この2つを組み合わせることが、現代的なPython開発の基本スタイルです。
\ No newline at end of file
+**`pyenv` でPythonバージョンを固定し、`venv` でプロジェクトのパッケージを隔離する** のが、現代的なPython開発の基本スタイルです。(前述の **Conda** は、このPythonバージョン管理と環境・パッケージ管理を両方とも行うことができます。)
\ No newline at end of file
diff --git a/public/docs/python-2.md b/public/docs/python-2.md
index a461c4d..6ff3215 100644
--- a/public/docs/python-2.md
+++ b/public/docs/python-2.md
@@ -1,182 +1,169 @@
# 第2章: Pythonの基本構文とデータ型
-他の言語での経験を活かし、Pythonの基本的な文法と組み込み型を素早くキャッチアップしましょう。特に、多くの静的型付け言語と異なる**インデントによるブロック表現**と**動的型付け**は、Pythonを理解する上で最初の重要なポイントです。
+他の言語でのプログラミング経験がある方を対象に、Pythonの基本的な文法と組み込みデータ型を解説します。多くの静的型付け言語(Java, C++, C\#など)との違いを意識しながら、Pythonの特徴を素早く掴んでいきましょう。特に、**動的型付け**は重要なコンセプトです。
-この章のコード例は、Pythonの対話モード(REPL)で `>>>` に続くコードを直接入力し、その直後に表示される結果を確認することを想定しています。
-
-
+この柔軟性は、静的型付け言語に慣れていると少し奇妙に感じるかもしれませんが、Pythonの簡潔さと書きやすさの源泉となっています。
-## 変数宣言と動的型付け
+## 基本的なデータ型
-静的型付け言語(Java, C++, C\#など)に慣れている方にとって、Pythonの変数宣言は非常にシンプルに感じられるでしょう。
+Pythonには多くの組み込みデータ型がありますが、ここでは最も基本的なものを紹介します。
-**Pythonでは、変数の型を事前に宣言する必要はありません**。変数への代入が初めて行われたときに、変数が作成され、代入された値の型が自動的にその変数の型となります。これを**動的型付け**と呼びます。
+### 数値(int, float)
-変数名だけを入力してEnterキーを押すと、その時点での変数の値を確認できます。
+Pythonは整数 (`int`) と浮動小数点数 (`float`) を区別します。
-```python
->>> # 変数 `count` は整数(int)型として自動的に解釈される
->>> count = 100
->>> count
-100
->>> type(count) # type()関数で現在の型を確認
+```python-repl
+>>> # 整数 (int)
+>>> a = 10
+>>> type(a)
-
->>> # 同じ変数に別の型(文字列)を再代入できる
->>> count = "百"
->>> count
-'百'
->>> type(count)
-
+>>> # 浮動小数点数 (float)
+>>> b = 3.14
+>>> type(b)
+
```
-## 基本的なデータ型
+四則演算は直感的に行えます。注意点として、除算 (`/`) は常に `float` を返します。整数除算を行いたい場合は (`//`) を使います。
-ここでは、プログラミングで頻繁に使用される基本的なデータ型を紹介します。対話モードでは、式を評価した結果が直接表示されます。
-
-### 数値 (`int`, `float`)
-
-Pythonには整数 (`int`) と浮動小数点数 (`float`) があります。型の区別は自動的に行われます。
-
-```python
->>> x = 10
->>> y = 3.14
-
->>> # 演算結果がそのまま表示される
->>> x + y
-13.14
->>> x * 2
-20
->>> x / 3 # 通常の除算
+```python-repl
+>>> 10 / 3
3.3333333333333335
->>> x // 3 # 整数の除算(切り捨て)
+>>> 10 // 3
3
->>> x % 3 # 剰余
+>>> # べき乗
+>>> 2 ** 4
+16
+>>> # 剰余
+>>> 10 % 3
1
->>> x ** 3 # べき乗
-1000
```
-### 文字列 (`str`)
+### 文字列(str)
-文字列はシングルクォート `'` またはダブルクォート `"` で囲みます。どちらを使っても機能的な違いはありません。
+文字列はシングルクォート (`'`) またはダブルクォート (`"`) で囲んで作成します。
-```python
->>> message1 = "こんにちは、世界"
->>> message2 = 'Hello, World!'
-
->>> # 文字列の連結
->>> greeting = message1 + " & " + message2
->>> greeting
-'こんにちは、世界 & Hello, World!'
-
->>> # 文字列の繰り返し
->>> "-" * 10
-'----------'
+```python-repl
+>>> name = "Guido"
+>>> greeting = 'Hello'
```
-#### f-stringによるフォーマット
-他の言語での文字列フォーマット(`printf` や `String.format`)に相当するものとして、Python 3.6以降では**f-string**が推奨されます。非常に直感的で強力です。
-
-文字列の前に `f` を置き、波括弧 `{}` の中に変数名や式を直接記述できます。
+文字列の連結は `+` 演算子、繰り返しは `*` 演算子を使います。
-```python
->>> name = "佐藤"
->>> age = 28
-
->>> # 従来の方法(少し面倒)
->>> "名前: " + name + ", 年齢: " + str(age)
-'名前: 佐藤, 年齢: 28'
+```python-repl
+>>> full_greeting = greeting + ", " + name + "!"
+>>> print(full_greeting)
+Hello, Guido!
+>>> print("-" * 10)
+----------
+```
->>> # f-stringを使って文字列を生成する
->>> profile = f"名前: {name}, 年齢: {age}"
->>> profile
-'名前: 佐藤, 年齢: 28'
+変数の値を文字列に埋め込む際には、**f-string (フォーマット済み文字列リテラル)** が非常に便利で推奨されています。文字列の前に `f` を付け、埋め込みたい変数を `{}` で囲みます。
->>> # f-string内では計算も可能
->>> f"{name}さんは10年後、{age + 10}歳です。"
-'佐藤さんは10年後、38歳です。'
+```python-repl
+>>> name = "Ada"
+>>> age = 36
+>>> message = f"My name is {name} and I am {age} years old."
+>>> print(message)
+My name is Ada and I am 36 years old.
```
-### 真偽値 (`bool`)
+### 真偽値(bool)
-真偽値は `True` または `False` の2つの値を持ちます。**先頭が大文字であることに注意してください**。
+真偽値は `True` と `False` の2つの値を持ちます(先頭が大文字であることに注意してください)。論理演算子には `and`, `or`, `not` を使います。
-```python
+```python-repl
>>> is_active = True
->>> is_admin = False
-
->>> # 論理演算子 (and, or, not)
->>> is_active and is_admin
+>>> has_permission = False
+>>> type(is_active)
+
+>>> # 論理積 (AND)
+>>> is_active and has_permission
False
->>> is_active or is_admin
+>>> # 論理和 (OR)
+>>> is_active or has_permission
True
+>>> # 否定 (NOT)
>>> not is_active
False
```
-## 型ヒント (Type Hints) の紹介
+## 型ヒント(Type Hints)の紹介
-動的型付けはコードを素早く書ける一方で、大規模なプロジェクトでは、関数がどのような型の引数を期待し、何を返すのかが分かりにくくなることがあります。
+動的型付けは柔軟ですが、コードが大規模になると変数がどの型を期待しているのかが分かりにくくなることがあります。そこで導入されたのが**型ヒント**です。これは、変数や関数の引数、返り値に「期待される型」を注釈として付与する機能です。
-そこでPython 3.5から導入されたのが**型ヒント**です。これは、変数や関数の引数、戻り値に「期待される型」を注釈として付与する機能です。
+`変数名: 型` のように記述します。
-**重要**: 型ヒントはあくまで「ヒント」であり、Pythonのインタプリタはこれを**実行時に強制しません**。しかし、エディタや静的解析ツールがこのヒントを解釈し、型の不一致を開発段階で警告してくれるため、コードの可読性と堅牢性が大幅に向上します。
+```python-repl
+>>> # 型ヒントを付けた変数宣言
+>>> user_name: str = "Alice"
+>>> user_id: int = 123
+```
-```python
->>> # nameはstr型、ageはint型を期待し、戻り値はstr型であることを示す
->>> def create_user_profile(name: str, age: int) -> str:
-... return f"ユーザー名: {name}, 年齢: {age}"
-...
->>> # 正しい使い方
->>> user1 = create_user_profile("田中", 35)
->>> user1
-'ユーザー名: 田中, 年齢: 35'
+**重要な注意点:** 型ヒントはあくまで「ヒント」であり、**Pythonの実行エンジンはこれを強制しません**。つまり、型ヒントと異なる型の値を代入してもエラーにはなりません。
->>> # 変数にも型ヒントを付けられる
->>> user_id: int = 101
+```python-repl
+>>> user_id: int = 123
+>>> type(user_id)
+
+>>> # int型とヒントを付けたが、文字列を代入できてしまう
+>>> user_id = "abc-789"
+>>> type(user_id)
+
+```
->>> # --- 型ヒントに反する使い方 ---
->>> # 静的解析ツールは警告を出す可能性があるが、実行はできてしまう
->>> user2 = create_user_profile("鈴木", "25歳") # ageに文字列を渡している
->>> user2
-'ユーザー名: 鈴木, 年齢: 25歳'
+型ヒントは、コードの可読性を高めたり、MyPyのような静的解析ツールや統合開発環境(IDE)がコードのバグを事前に発見したりするために利用されます。
+
+## この章のまとめ
+
+ * Pythonでは変数の型宣言は不要で、値の代入によって型が自動的に決まる(**動的型付け**)。
+ * 基本的なデータ型として**数値** (`int`, `float`)、**文字列** (`str`)、**真偽値** (`bool`) がある。
+ * 文字列に変数を埋め込むには、簡潔で強力な **f-string** を使うのが一般的。
+ * **型ヒント** (`変数名: 型`) は、コードの可読性を向上させるための注釈であり、実行時に型の強制力はない。
+
+### 練習問題1
+
+`item_name` という変数に商品名(文字列)、`price` という変数に価格(整数)、`stock` という変数に在庫数(整数)をそれぞれ代入してください。その後、f-stringを使って「商品: [商品名], 価格: [価格]円, 在庫: [在庫数]個」という形式の文字列にし、 `print()` で出力するコードを書いてみましょう。
+
+```python:practice2_1.py
```
-静的型付け言語の経験者にとって、型ヒントは馴染みやすく、動的言語の柔軟性と静的解析の安全性を両立させるための強力なツールとなるでしょう。
+```python-exec:practice2_1.py
+(出力例) 商品: 高性能マウス, 価格: 4500円, 在庫: 2個
+```
+
+### 練習問題2
------
+`is_adult` という変数に `bool` 型の型ヒントを付けて `True` を代入し、`type()` で型を確認してください。その後、同じ変数に数値の `20` を代入し、再度 `type()` で型を確認してください。この結果から、型ヒントが実行時の動作にどのような影響を与える(あるいは与えない)か考察してみましょう。
-この章では、Pythonの構文の基礎となるインデントルール、柔軟な動的型付け、そして基本的なデータ型について対話モードで確認しながら学びました。特にf-stringと型ヒントは、モダンなPython開発における必須知識です。
+```python:practice2_2.py
+```
-次の章では、複数の要素をまとめて扱うための強力なデータ構造である**リスト**と**タプル**について詳しく見ていきます。
\ No newline at end of file
+```python-exec:practice2_2.py
+```
diff --git a/public/docs/python-3.md b/public/docs/python-3.md
index 66bd282..c3c3eb9 100644
--- a/public/docs/python-3.md
+++ b/public/docs/python-3.md
@@ -12,7 +12,7 @@ Pythonのプログラミングにおいて、データを効率的に扱う能
**基本的な使い方 (REPL実行例)**
-```python
+```python-repl
>>> # リストの作成
>>> fruits = ['apple', 'banana', 'cherry']
>>> fruits
@@ -57,7 +57,7 @@ Pythonのプログラミングにおいて、データを効率的に扱う能
**基本的な使い方 (REPL実行例)**
-```python
+```python-repl
>>> # タプルの作成 (丸括弧を使用)
>>> coordinates = (10, 20)
>>> coordinates
@@ -92,7 +92,7 @@ TypeError: 'tuple' object does not support item assignment
**基本的な使い方 (REPL実行例)**
-```python
+```python-repl
>>> # 辞書の作成
>>> person = {'name': 'Taro Yamada', 'age': 30, 'city': 'Tokyo'}
>>> person
@@ -132,7 +132,7 @@ dict_items([('name', 'Taro Yamada'), ('age', 31), ('city', 'Tokyo'), ('job', 'En
**基本的な使い方 (REPL実行例)**
-```python
+```python-repl
>>> # セットの作成 (重複した4は自動的に無視される)
>>> numbers = {1, 2, 3, 4, 4, 5}
>>> numbers
@@ -168,7 +168,7 @@ dict_items([('name', 'Taro Yamada'), ('age', 31), ('city', 'Tokyo'), ('job', 'En
**REPL実行例**
-```python
+```python-repl
>>> numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> # インデックス1から4の手前まで
@@ -200,7 +200,7 @@ dict_items([('name', 'Taro Yamada'), ('age', 31), ('city', 'Tokyo'), ('job', 'En
`for`ループで書く場合と、リスト内包表記で書く場合を比較してみましょう。
-```python
+```python-repl
>>> # forループの場合
>>> squares_loop = []
>>> for i in range(10):
@@ -222,7 +222,7 @@ dict_items([('name', 'Taro Yamada'), ('age', 31), ('city', 'Tokyo'), ('job', 'En
**辞書内包表記**
-```python
+```python-repl
>>> # 数値をキー、その2乗を値とする辞書を作成
>>> square_dict = {x: x*x for x in range(5)}
>>> square_dict
@@ -231,7 +231,7 @@ dict_items([('name', 'Taro Yamada'), ('age', 31), ('city', 'Tokyo'), ('job', 'En
**セット内包表記**
-```python
+```python-repl
>>> # リスト内のユニークな数値の2乗のセットを作成
>>> numbers = [1, 2, 2, 3, 4, 4, 5]
>>> square_set = {x*x for x in numbers}
@@ -239,4 +239,61 @@ dict_items([('name', 'Taro Yamada'), ('age', 31), ('city', 'Tokyo'), ('job', 'En
{1, 4, 9, 16, 25}
```
-内包表記は、Pythonプログラマーにとって必須のテクニックです。積極的に活用して、効率的で美しいコードを目指しましょう。
\ No newline at end of file
+## この章のまとめ
+
+この章では、Pythonでデータを扱うための基本的な4つのコレクションを学びました。それぞれの特性を理解し、状況に応じて適切に使い分けることが重要です。
+
+| データ構造 | 構文例 | 変更可能性 | 順序 | 重複 | 主な用途 |
+| :--- | :--- | :--- | :--- | :--- | :--- |
+| **リスト (List)** | `[1, 'a', 2]` | **可能** (ミュータブル) | **あり** | 許可 | 順序があり、変更が必要な要素の集まり。 |
+| **タプル (Tuple)** | `(1, 'a', 2)` | **不可能** (イミュータブル) | **あり** | 許可 | 変更しない(させたくない)データの集まり、辞書のキー。 |
+| **辞書 (Dictionary)** | `{'key': 'value'}` | **可能** (ミュータブル) | **あり** (Python 3.7+) | キーは不許可 | キーと値のペアでデータを管理。 |
+| **セット (Set)** | `{1, 'a', 2}` | **可能** (ミュータブル) | **なし** | 不許可 | 重複を除き、要素の存在確認や集合演算を高速に行う。 |
+
+加えて、**スライシング**を使えばシーケンス(リストやタプル)から部分的な要素を柔軟に取得でき、**内包表記**を利用すれば、これらのコレクションを一行で効率的かつPythonらしく生成できます。これらのツールは、あなたのコードをより簡潔で強力なものにしてくれるでしょう。
+
+### 練習問題1: 商品のフィルタリング
+
+ある店舗の商品のリストがあります。このリストから、価格が500円以上の商品だけを抽出し、その名前だけを新しいリストに格納してください。
+
+**ヒント:**
+リスト内包表記と、辞書の値にアクセスする方法 (`product['price']`) を組み合わせ、`if` 条件を追加してみましょう。
+
+```python:practice3_1.py
+products = [
+ {'name': 'Apple', 'price': 150},
+ {'name': 'Banana', 'price': 100},
+ {'name': 'Melon', 'price': 600},
+ {'name': 'Orange', 'price': 120},
+ {'name': 'Grape', 'price': 550}
+]
+
+```
+
+```python-exec:practice3_1.py
+(出力例) ['Melon', 'Grape']
+```
+
+### 練習問題2: クラブ活動のメンバー分析
+
+2つのクラブ活動、「数学クラブ」と「科学クラブ」のメンバーリストがあります。セット(集合)の機能を使って、以下のメンバーリストを作成してください。
+
+a. 両方のクラブに所属しているメンバー
+b. 少なくともどちらか一方のクラブに所属している全メンバー
+c. 数学クラブにのみ所属しているメンバー
+
+**ヒント:**
+セットの積集合 (`&`)、和集合 (`|`)、差集合 (`-`) 演算子を使います。
+
+```python:practice3_2.py
+math_club = {'Alice', 'Bob', 'Charlie', 'David'}
+science_club = {'Charlie', 'David', 'Eve', 'Frank'}
+
+```
+
+```python-exec:practice3_2.py
+(出力例)
+a. {'Charlie', 'David'}
+b. {'Alice', 'Bob', 'Charlie', 'David', 'Eve', 'Frank'}
+c. {'Alice', 'Bob'}
+```
diff --git a/public/docs/python-4.md b/public/docs/python-4.md
index 4cbdc94..9d2bc39 100644
--- a/public/docs/python-4.md
+++ b/public/docs/python-4.md
@@ -6,7 +6,7 @@
Pythonの条件分岐は`if`、`elif`(else ifの略)、`else`を使って記述します。C言語やJavaのような波括弧`{}`は使わず、**コロン`:`とインデント(通常は半角スペース4つ)**でコードブロックを表現するのが最大の特徴です。
-```python
+```python-repl
>>> score = 85
>>> if score >= 90:
... print('優')
@@ -22,7 +22,7 @@ Pythonの条件分岐は`if`、`elif`(else ifの略)、`else`を使って記
条件式に`and`や`or`、`not`といった論理演算子も使用できます。
-```python
+```python-repl
>>> temp = 25
>>> is_sunny = True
>>> if temp > 20 and is_sunny:
@@ -35,7 +35,7 @@ Pythonの条件分岐は`if`、`elif`(else ifの略)、`else`を使って記
Pythonの`for`ループは、他の言語の`for (int i = 0; i < 5; i++)`といったカウンタ変数を使うスタイルとは少し異なります。リストやタプル、文字列などの**イテラブル(反復可能)オブジェクト**から要素を1つずつ取り出して処理を実行します。これは、Javaの拡張for文やC\#の`foreach`に似ています。
-```python
+```python-repl
>>> fruits = ['apple', 'banana', 'cherry']
>>> for fruit in fruits:
... print(f"I like {fruit}")
@@ -49,7 +49,7 @@ I like cherry
決まった回数のループを実行したい場合は、`range()`関数が便利です。`range(n)`は0からn-1までの連続した数値を生成します。
-```python
+```python-repl
>>> for i in range(5):
... print(i)
...
@@ -64,7 +64,7 @@ I like cherry
ループ処理の中で、要素のインデックス(番号)と値の両方を使いたい場合があります。そのような時は`enumerate()`関数を使うと、コードが非常にスッキリします。これは非常にPythonらしい書き方の一つです。
-```python
+```python-repl
>>> fruits = ['apple', 'banana', 'cherry']
>>> for i, fruit in enumerate(fruits):
... print(f"Index: {i}, Value: {fruit}")
@@ -78,7 +78,7 @@ Index: 2, Value: cherry
`while`ループは、指定された条件が`True`である間、処理を繰り返します。ループを途中で抜けたい場合は`break`を、現在の回の処理をスキップして次の回に進みたい場合は`continue`を使用します。
-```python
+```python-repl
>>> n = 0
>>> while n < 5:
... print(n)
@@ -95,7 +95,7 @@ Index: 2, Value: cherry
関数は`def`キーワードを使って定義します。ここでもコードブロックはコロン`:`とインデントで示します。値は`return`キーワードで返します。
-```python
+```python-repl
>>> def greet(name):
... """指定された名前で挨拶を返す関数""" # これはDocstringと呼ばれるドキュメント文字列です
... return f"Hello, {name}!"
@@ -105,31 +105,44 @@ Index: 2, Value: cherry
Hello, Alice!
```
+引数と返り値に**型アノテーション(型ヒント)**を付けることもできます。これはコードの可読性を高め、静的解析ツールによるバグの発見を助けますが、実行時の動作に直接影響を与えるものではありません。
+型アノテーションは `引数名: 型` のように記述し、返り値の型は `-> 型:` のように記述します。
+
+```python-repl
+>>> # typingモジュールからList型をインポート
+>>> from typing import List
+>>> def greet(name: str) -> str:
+... """指定された名前で挨拶を返す関数"""
+... return f"Hello, {name}!"
+...
+>>> message = greet("Alice")
+>>> print(message)
+Hello, Alice!
+```
+
## 引数の渡し方(位置引数、キーワード引数、デフォルト引数値)
-Pythonの関数は、非常に柔軟な引数の渡し方ができます。
+Pythonの関数は、非常に柔軟な引数の渡し方ができます。型アノテーションと組み合わせることで、どのような型の引数を期待しているかがより明確になります。
-* **位置引数 (Positional Arguments):** 最も基本的な渡し方で、定義された順序で値を渡します。
-* **キーワード引数 (Keyword Arguments):** `引数名=値`の形式で渡します。順序を問わないため、可読性が向上します。
-* **デフォルト引数値 (Default Argument Values):** 関数を定義する際に引数にデフォルト値を設定できます。呼び出し時にその引数が省略されると、デフォルト値が使われます。
+ * **位置引数 (Positional Arguments):** 最も基本的な渡し方で、定義された順序で値を渡します。
+ * **キーワード引数 (Keyword Arguments):** `引数名=値`の形式で渡します。順序を問わないため、可読性が向上します。
+ * **デフォルト引数値 (Default Argument Values):** 関数を定義する際に引数にデフォルト値を設定できます。呼び出し時にその引数が省略されると、デフォルト値が使われます。
-```python
->>> def describe_pet(animal_type, pet_name, owner_name="Taro"):
+```python-repl
+>>> def describe_pet(animal_type: str, pet_name: str, owner_name: str = "Taro") -> None:
+... # この関数は何も値を返さないため、返り値の型は None となります
... print(f"私には {animal_type} がいます。")
... print(f"名前は {pet_name} で、飼い主は {owner_name} です。")
...
-
-# 位置引数のみで呼び出し
+>>> # 位置引数のみで呼び出し
>>> describe_pet("ハムスター", "ジャンボ")
私には ハムスター がいます。
名前は ジャンボ で、飼い主は Taro です。
-
-# キーワード引数で呼び出し(順序を逆にしてもOK)
+>>> # キーワード引数で呼び出し(順序を逆にしてもOK)
>>> describe_pet(pet_name="ポチ", animal_type="犬")
私には 犬 がいます。
名前は ポチ で、飼い主は Taro です。
-
-# デフォルト引数を持つ引数を指定して呼び出し
+>>> # デフォルト引数を持つ引数を指定して呼び出し
>>> describe_pet("猫", "ミケ", "Hanako")
私には 猫 がいます。
名前は ミケ で、飼い主は Hanako です。
@@ -137,16 +150,16 @@ Pythonの関数は、非常に柔軟な引数の渡し方ができます。
**注意点:** デフォルト引数を持つ引数は、持たない引数の後に定義する必要があります。
-## 可変長引数 (\*args, \*\*kwargs)
+## 可変長引数 (*args, **kwargs)
-関数の引数の数が可変である場合に対応するための仕組みです。
+関数の引数の数が可変である場合に対応するための仕組みです。型アノテーションを使う場合は、`typing`モジュールから`Any`などをインポートすると便利です。
### `*args`
-任意の数の**位置引数**をタプルとして受け取ります。慣習的に`args`という名前が使われます。
+任意の数の**位置引数**をタプルとして受け取ります。型アノテーションでは `*args: 型` のように表現します。
-```python
->>> def sum_all(*numbers):
+```python-repl
+>>> def sum_all(*numbers: int) -> int:
... print(f"受け取ったタプル: {numbers}")
... total = 0
... for num in numbers:
@@ -163,10 +176,11 @@ Pythonの関数は、非常に柔軟な引数の渡し方ができます。
### `**kwargs`
-任意の数の**キーワード引数**を辞書として受け取ります。慣習的に`kwargs` (keyword arguments) という名前が使われます。
+任意の数の**キーワード引数**を辞書として受け取ります。型アノテーションでは `**kwargs: 型` のように表現します。どのような型の値も受け付ける場合は `Any` を使います。
-```python
->>> def print_profile(**user_info):
+```python-repl
+>>> from typing import Any
+>>> def print_profile(**user_info: Any) -> None:
... print(f"受け取った辞書: {user_info}")
... for key, value in user_info.items():
... print(f"{key}: {value}")
@@ -180,25 +194,91 @@ city: Tokyo
## ラムダ式(Lambda expressions)
-`lambda`キーワードを使うと、名前のない小さな**無名関数**を定義できます。複雑な処理には向きませんが、`sorted`関数のキーを指定したり、GUIのコールバック関数を定義したりと、簡単な処理をその場で記述したい場合に非常に便利です。
+`lambda`キーワードを使うと、名前のない小さな**無名関数**を定義できます。
構文: `lambda 引数: 式`
-```python
-# 通常の関数で2つの数を足す
->>> def add(x, y):
+```python-repl
+>>> # 通常の関数で2つの数を足す
+>>> def add(x: int, y: int) -> int:
... return x + y
...
-
-# ラムダ式で同じ処理を定義
+>>> # ラムダ式で同じ処理を定義
>>> add_lambda = lambda x, y: x + y
>>> print(add_lambda(3, 5))
8
-
-# sorted関数のキーとして利用する例
+>>> # sorted関数のキーとして利用する例
>>> students = [('Taro', 80), ('Jiro', 95), ('Saburo', 75)]
-# 成績(タプルの2番目の要素)でソートする
+>>> # 成績(タプルの2番目の要素)でソートする
>>> sorted_students = sorted(students, key=lambda student: student[1], reverse=True)
>>> print(sorted_students)
[('Jiro', 95), ('Taro', 80), ('Saburo', 75)]
```
+
+## この章のまとめ
+
+この章では、Pythonの制御構文と関数の基本を学びました。他の言語の経験がある方にとって、特に以下の点はPythonの特徴として重要です。
+
+ * **インデントが構文の一部**: 波括弧`{}`の代わりにインデントでコードブロックを定義するため、自然と誰が書いても読みやすいコードスタイルになります。
+ * **`for`ループはイテラブルを巡る**: `for item in collection:` の形が基本です。インデックスが必要な場合は、`for i, item in enumerate(collection):` のように`enumerate()`を使うのがPythonらしい書き方です。
+ * **柔軟な関数引数**: **キーワード引数**、**デフォルト引数値**、そして**可変長引数 (`*args`, `**kwargs`)** を使いこなすことで、非常に柔軟で再利用性の高い関数を作成できます。
+ * **型アノテーション**: 引数や返り値に型ヒントを付けることで、関数の意図が明確になり、コードの信頼性が向上します。
+ * **ラムダ式**: ちょっとした処理をその場で関数として渡したい場合に、`lambda`はコードを簡潔に保つのに役立ちます。
+
+これらの機能を理解し使いこなすことが、より効率的で「Pythonic(パイソニック)」なコードを書くための第一歩となります。
+
+### 練習問題1: 偶数とそのインデックスの発見
+
+数値のリストが与えられたとき、そのリストに含まれる**偶数**とその**インデックス(位置番号)**だけを出力するプログラムを書いてください。
+
+**ヒント:**
+
+ * `for`ループと`enumerate()`を組み合わせます。
+ * 数値が偶数かどうかは、`%`(剰余)演算子を使って、2で割った余りが0になるかで判定できます (`number % 2 == 0`)。
+
+```python:practice4_1.py
+numbers: list[int] = [8, 15, 22, 37, 40, 51, 68]
+
+```
+
+```python-exec:practice4_1.py
+(出力例)
+インデックス: 0, 値: 8
+インデックス: 2, 値: 22
+インデックス: 4, 値: 40
+インデックス: 6, 値: 68
+```
+
+### 練習問題2: ユーザープロフィール作成関数
+
+ユーザーのプロフィール情報を出力する関数 `create_profile` を作成してください。引数と返り値には型アノテーションを付けてください。
+
+**要件:**
+
+1. `name`(名前)は`str`型で、必須の引数とします。
+2. `age`(年齢)と `city`(都市)は`str`型で、キーワード引数として任意に受け取れるようにします。もし指定されなかった場合は、年齢は「秘密」、都市は「不明」と表示されるようにしてください。
+3. この関数は値を返さないものとします。
+4. 関数を呼び出し、異なるパターンでプロフィールが出力されることを確認してください。
+
+**ヒント:**
+
+ * `age`と`city`にはデフォルト引数値を設定します。
+ * 値を返さない関数の返り値の型アノテーションは `-> None` です。
+
+```python:practice4_2.py
+def create_profile(
+```
+
+```python-exec:practice4_2.py
+(出力例)
+--- プロフィール ---
+名前: Tanaka
+年齢: 秘密
+都市: 不明
+--------------------
+--- プロフィール ---
+名前: Sato
+年齢: 32
+都市: Osaka
+--------------------
+```
diff --git a/public/docs/python-5.md b/public/docs/python-5.md
index 324ac6d..0d333ac 100644
--- a/public/docs/python-5.md
+++ b/public/docs/python-5.md
@@ -2,59 +2,28 @@
プログラミングを進めていくと、コードは必然的に長くなり、一つのファイルで管理するのが難しくなってきます。機能ごとにファイルを分割し、再利用しやすく、メンテナンスしやすい構造にすることが、効率的な開発の鍵となります。この章では、Pythonでコードを整理するための**モジュール**と**パッケージ**という仕組み、そしてPythonの強力な武器である**標準ライブラリ**の活用方法について学びます。
-
-## REPLからスクリプトファイルへ
-
-これまでの章では、コマンドを1行ずつ入力してすぐに結果が返ってくる**REPL (対話型実行環境)** を使ってきました。REPLは、ちょっとしたコードの動作確認や学習には非常に便利です。しかし、REPLを終了すると入力したコードは消えてしまいますし、複雑なプログラムを作るのにも向いていません。
-
-実際の開発では、プログラムを**スクリプトファイル**に保存して実行するのが一般的です。
-
-### Pythonファイルの作成と実行
-
-1. **ファイルの作成**:
- お使いのテキストエディタ(VS Code、サクラエディタ、メモ帳など何でも構いません)を開き、以下のコードを記述してください。そして、`hello.py` という名前で保存します。ファイルの拡張子は必ず `.py` にしてください。
-
- ```python
- # hello.py
- message = "Hello, Python Script!"
- print(message)
- print(f"2 + 3 = {2 + 3}")
- ```
-
-2. **ファイルの実行**:
- 次に、ターミナル(WindowsではコマンドプロンプトやPowerShell)を開き、`cd` コマンドで `hello.py` を保存したディレクトリに移動します。そして、以下のコマンドを実行してください。
-
- ```bash
- # python3コマンドの場合もあります
- python hello.py
- ```
-
- すると、画面に以下のように出力されるはずです。
-
- ```
- Hello, Python Script!
- 2 + 3 = 5
- ```
-
-このように、`python [ファイル名]` というコマンドで、ファイルに書かれたコードを上から順に実行することができます。これからは、このファイルベースでの開発を基本として進めていきましょう。
-
-
## モジュール:コードを部品化する
-さて、ここからが本題です。Pythonでは、先ほど作成した **`hello.py` のような `.py` ファイルが1つのモジュール**として扱われます。モジュールを使うことで、関連する関数やクラスを一つのファイルにまとめ、他のプログラムから再利用可能な「部品」として扱うことができます。これは、他の言語におけるライブラリやソースファイルのインポート機能に似ています。
+Pythonでは、**1つの `.py` ファイルが1つのモジュール**として扱われます。モジュールを使うことで、関連する関数やクラスを一つのファイルにまとめ、他のプログラムから再利用可能な「部品」として扱うことができます。これは、他の言語におけるライブラリやソースファイルのインポート機能に似ています。
### `import`文の基本
-モジュールを利用するには `import` 文を使います。Pythonには多くの便利なモジュールが標準で用意されています(これらを**標準ライブラリ**と呼びます)。例えば、数学的な計算を行う `math` モジュールをREPLで使ってみましょう。
+モジュールを利用するには `import` 文を使います。Pythonには多くの便利なモジュールが標準で用意されています(これらを**標準ライブラリ**と呼びます)。例えば、数学的な計算を行う `math` モジュールを使ってみましょう。
-```python
+```python-repl
>>> # mathモジュールをインポート
>>> import math
->>>
>>> # mathモジュール内の変数や関数を利用する
->>> print(math.pi)
+>>> print(math.pi) # 円周率π
+3.141592653589793
+>>> print(math.sqrt(16)) # 16の平方根
+4.0
+```
+
+出力:
+
+```
3.141592653589793
->>> print(math.sqrt(16))
4.0
```
@@ -62,7 +31,7 @@
* **`from ... import ...`**: モジュールから特定の関数や変数だけを取り込む
- ```python
+ ```python-repl
>>> from math import pi, sqrt
>>>
>>> print(pi) # 直接piを参照できる
@@ -73,7 +42,7 @@
* **`as` (別名)**: モジュールに別名をつけて利用する
- ```python
+ ```python-repl
>>> import math as m
>>>
>>> print(m.pi)
@@ -91,9 +60,7 @@
1. **`utils.py` の作成**:
まず、便利な関数をまとめた `utils.py` というファイルを作成します。
- ```python
- # utils.py
-
+ ```python:utils.py
def say_hello(name):
"""指定された名前で挨拶を返す"""
return f"Hello, {name}!"
@@ -112,6 +79,12 @@
print(f"Average: {avg}")
```
+ ```python-exec:utils.py
+ This is a utility module.
+ Hello, World!
+ Average: 20.0
+ ```
+
> **`if __name__ == "__main__":` の重要性**
> この記述はPythonの定型句です。
@@ -122,18 +95,21 @@
2. **`main.py` からの利用**:
次に、`utils.py` と同じディレクトリに `main.py` を作成し、`utils` モジュールをインポートして使います。
- ```python
- # main.py
-
+ ```python:main.py
# 自作のutilsモジュールをインポート
import utils
greeting = utils.say_hello("Alice")
- print(greeting) # => Hello, Alice!
+ print(greeting)
scores = [88, 92, 75, 100]
average_score = utils.get_list_average(scores)
- print(f"Your average score is: {average_score}") # => Your average score is: 88.75
+ print(f"Your average score is: {average_score}")
+ ```
+
+ ```python-exec:main.py
+ Hello, Alice!
+ Your average score is: 88.75
```
このように、機能ごとにファイルを分割することで、コードの見通しが良くなり、再利用も簡単になります。
@@ -165,8 +141,6 @@ my_project/
`main.py` からこれらのモジュールをインポートするには、`パッケージ名.モジュール名` のように記述します。
```python
-# main.py
-
# パッケージ内のモジュールをインポート
from my_app import services
@@ -175,8 +149,28 @@ from my_app import services
# print(user_data)
```
-`__init__.py` には、パッケージがインポートされた際の初期化処理を記述することもできます。
+`__init__.py` には、パッケージがインポートされた際の初期化処理を記述することもできます。例えば、特定のモジュールから関数をパッケージのトップレベルにインポートしておくと、利用側でより短い記述でアクセスできるようになります。
+
+```python
+# my_app/__init__.py
+# servicesモジュールからfetch_user_data関数をインポート
+from .services import fetch_user_data
+
+print("my_app package has been initialized.")
+```
+
+このようにしておくと、`main.py` から以下のように直接関数をインポートできます。
+
+```python
+# main.py
+
+# __init__.pyで設定したおかげで、短いパスでインポートできる
+from my_app import fetch_user_data
+
+user_data = fetch_user_data(user_id=123)
+print(user_data)
+```
## 標準ライブラリ:Pythonに備わった強力なツール群
@@ -190,7 +184,7 @@ Pythonの大きな魅力の一つは、その「**バッテリー同梱 (Batteri
また、REPLの `help()` や `dir()` を使うと、モジュールの内容を簡単に確認できます。
-```python
+```python-repl
>>> import datetime
>>> # datetimeモジュールが持つ属性や関数のリストを表示
>>> dir(datetime)
@@ -204,63 +198,53 @@ class date(builtins.object)
| date(year, month, day) --> a date object
|
| Methods defined here:
-... (ヘルプ情報が続く) ...
+(ヘルプ情報が続く) ...
```
### よく使われる標準ライブラリの例
-ここでは、日常的によく使われる標準ライブラリをいくつかREPLで試してみましょう。
+ここでは、日常的によく使われる標準ライブラリをいくつか紹介します。
- * **`os`**: OSとの対話。ファイルやディレクトリの操作など。
+ * **`os`**: オペレーティングシステムと対話するための機能を提供します。ファイルやディレクトリの操作、環境変数の取得などができます。
- ```python
+ ```python-repl
>>> import os
- >>>
>>> # カレントディレクトリのファイル一覧を取得
>>> os.listdir('.')
['hello.py', 'utils.py', 'main.py']
- >>>
>>> # OSに依存しない安全なパスの結合
- >>> os.path.join('data', 'file.txt')
- 'data/file.txt' # Mac/Linuxの場合
- # 'data\\file.txt' # Windowsの場合
+ >>> os.path.join('data', 'file.txt') # Windowsなら 'data\\file.txt'
+ 'data/file.txt'
```
- * **`sys`**: Pythonインタプリタに関する情報。コマンドライン引数など。
+ * **`sys`**: Pythonインタプリタ自体を制御するための機能を提供します。コマンドライン引数の取得や、Pythonの検索パスの確認などができます。
- ```python
+ ```python-repl
>>> import sys
- >>>
>>> # Pythonのバージョンを表示
- >>> sys.version
- '3.11.4 (main, Jun 7 2023, 10:13:09) [GCC 12.3.0]' # 環境により異なります
+ >>> sys.version # 環境により異なります
+ '3.11.4 (main, Jun 7 2023, 10:13:09) [GCC 12.3.0]'
```
- ( `sys.argv` はスクリプト実行時に意味を持つため、ここでは割愛します )
+ * **`datetime`**: 日付や時刻を扱うための機能を提供します。
- * **`datetime`**: 日付や時刻の操作。
-
- ```python
+ ```python-repl
>>> import datetime
- >>>
- >>> # 現在の日時を取得
+ >>> # 現在の日時を取得 (実行時刻による)
>>> now = datetime.datetime.now()
>>> print(now)
- 2025-08-12 18:26:06.123456 # 実行時刻による
- >>>
+ 2025-08-12 18:26:06.123456
>>> # 日時をフォーマットして文字列にする
>>> now.strftime('%Y-%m-%d %H:%M:%S')
'2025-08-12 18:26:06'
```
- * **`json`**: JSON形式のデータの操作。
+ * **`json`**: Web APIなどで広く使われているデータ形式であるJSONを扱うための機能を提供します。
- ```python
+ ```python-repl
>>> import json
- >>>
>>> # Pythonの辞書型データ
>>> user = {"id": 1, "name": "Ken", "email": "ken@example.com"}
- >>>
>>> # 辞書型をJSON形式の文字列に変換 (dumps: dump string)
>>> json_string = json.dumps(user, indent=2)
>>> print(json_string)
@@ -269,24 +253,81 @@ class date(builtins.object)
"name": "Ken",
"email": "ken@example.com"
}
- >>>
>>> # JSON形式の文字列をPythonの辞書型に変換 (loads: load string)
>>> loaded_user = json.loads(json_string)
>>> loaded_user['name']
'Ken'
```
-これらの他にも、正規表現を扱う `re`、乱数を生成する `random` など、数え切れないほどの便利なモジュールが標準で提供されています。何かを実装したいと思ったら、まずは「Python 標準ライブラリ 〇〇」で検索してみると、車輪の再発明を防ぐことができます。
+これらの他にも、正規表現を扱う `re`、乱数を生成する `random`、HTTPリクエストを送信する `urllib.request` など、数え切れないほどの便利なモジュールが標準で提供されています。何かを実装したいと思ったら、まずは「Python 標準ライブラリ 〇〇」で検索してみると、車輪の再発明を防ぐことができます。
+
+## この章のまとめ
+この章では、Pythonのコードが複雑になるにつれて重要性を増す、整理と再利用のテクニックを学びました。ここで学んだ概念は、小さなスクリプトから大規模なアプリケーションまで、あらゆるレベルのPythonプログラミングで役立ちます。
-## まとめ
+ * **モジュール**: 1つの `.py` ファイルは1つの**モジュール**です。関連する関数やクラスをモジュールにまとめることで、コードを論理的な単位に分割できます。他のファイルからは `import` 文を使ってその機能を再利用できます。
+ * **パッケージ**: **パッケージ**は、複数のモジュールを階層的なディレクトリ構造で管理する仕組みです。これにより、大規模なプロジェクトでも名前の衝突を避け、関連するコードをまとめて整理することができます。
+ * **標準ライブラリ**: Pythonには「**バッテリー同梱**」という思想があり、`datetime` (日時)、`os` (OS機能)、`json` (データ形式) など、すぐに使える便利なモジュールが豊富に揃っています。これらを活用することで、開発を大幅に効率化できます。
-この章では、Pythonにおけるコードの整理術を学びました。
+### 練習問題1: 計算モジュールを作ろう 🔢
- * **スクリプトファイル (`.py`)** にコードを保存し、`python` コマンドで実行するのが開発の基本です。
- * **モジュール**は `.py` ファイル単位のコードの部品であり、`import` 文で再利用します。
- * **パッケージ**はモジュールをまとめるディレクトリであり、大規模なプロジェクトの構造を整理します。
- * **`if __name__ == "__main__":`** は、モジュールが直接実行されたか、インポートされたかを判別するための重要な構文です。
- * Pythonには強力な**標準ライブラリ**が多数同梱されており、多くの一般的なタスクはこれらを活用することで効率的に実装できます。
+四則演算を行うための自作モジュールを作成し、別のファイルから利用してみましょう。
-コードを適切にモジュール化・パッケージ化することは、読みやすく、テストしやすく、再利用しやすい、質の高いソフトウェアを開発するための第一歩です。
\ No newline at end of file
+1. `calculator.py` というファイルを作成し、以下の4つの関数を定義してください。
+ * `add(a, b)`: aとbの和を返す
+ * `subtract(a, b)`: aとbの差を返す
+ * `multiply(a, b)`: aとbの積を返す
+ * `divide(a, b)`: aをbで割った商を返す。ただし、`b` が `0` の場合は「ゼロで割ることはできません」という文字列を返すようにしてください。
+2. `practice5_1.py` というファイルを作成し、作成した `calculator` モジュールをインポートします。
+3. `practice5_1.py` の中で、`calculator` モジュールの各関数を少なくとも1回ずつ呼び出し、結果を `print` 関数で表示してください。
+
+```python:calculator.py
+def add(a, b):
+
+```
+
+```python:practice5_1.py
+```
+
+```python-exec:practice5_1.py
+(出力例)
+10 + 5 = 15
+10 - 5 = 5
+10 * 5 = 50
+10 / 2 = 5.0
+10 / 0 = ゼロで割ることはできません
+```
+
+### 練習問題2:日報データをJSONで作成しよう 📝
+
+標準ライブラリの `datetime` と `json` を使って、簡単な日報データを作成するプログラムを書いてみましょう。
+
+1. Pythonスクリプトを作成します。
+2. `datetime` モジュールを使って、**現在の日付**を `YYYY-MM-DD` 形式の文字列として取得します。
+3. 以下の情報を含むPythonの辞書を作成します。
+ * `author`: あなたの名前 (文字列)
+ * `date`: 手順2で取得した日付文字列
+ * `tasks`: その日に行ったタスクのリスト (例: `["会議", "資料作成", "メール返信"]`)
+4. `json` モジュールを使い、手順3で作成した辞書を人間が読みやすい形式 (インデント付き) のJSON文字列に変換します。
+5. 変換後のJSON文字列を `print` 関数で表示してください。
+
+**ヒント**: `datetime.datetime.now()` で現在時刻を取得し、`.strftime('%Y-%m-%d')` メソッドで日付をフォーマットできます。`json.dumps()` の `indent` 引数を指定すると、出力がきれになります。
+
+```python:practice5_2.py
+import datetime
+import json
+```
+
+```python-exec:practice5_2.py
+(出力例)
+{
+ "author": "山田 太郎",
+ "date": "2025-08-17",
+ "tasks": [
+ "Pythonのモジュール学習",
+ "練習問題の実装",
+ "チームミーティング"
+ ],
+ "status": "完了"
+}
+```
diff --git a/public/docs/python-6.md b/public/docs/python-6.md
index ebcc733..c1c3b22 100644
--- a/public/docs/python-6.md
+++ b/public/docs/python-6.md
@@ -8,201 +8,305 @@ Pythonでは、`class`キーワードを使ってクラスを定義します。J
クラスを定義したら、関数を呼び出すように`クラス名()`と書くことで、そのクラスの**インスタンス**(オブジェクト)を生成できます。
-```python
->>> # 中身が何もない、最もシンプルなクラスを定義
->>> class User:
-... pass
-...
->>> # Userクラスのインスタンスを生成
->>> user1 = User()
->>>
->>> # user1はUserクラスのオブジェクト(インスタンス)であることがわかる
->>> user1
-<__main__.User object at 0x10e85a4d0>
+```python:dog1.py
+class Dog:
+ pass # passは何もしないことを示す文
+
+# Dogクラスのインスタンスを作成
+my_dog = Dog()
+
+print(my_dog)
+```
+
+```python-exec:dog1.py
+<__main__.Dog object at 0x10e85a4d0>
```
## コンストラクタ (`__init__`) と `self`
-インスタンスが生成される際に、特定の初期化処理を行いたい場合があります。そのために使われるのが`__init__`という特殊なメソッドで、他の言語における**コンストラクタ**に相当します。
-
-`__init__`メソッドの最初の引数には、慣習的に`self`という名前を付けます。この`self`は、生成されるインスタンス自身を指す参照で、JavaやJavaScriptの`this`と同じ役割を果たします。`self`を通じて、インスタンスに固有のデータを格納する**インスタンス変数**を定義します。
-
-```python
->>> class Dog:
-... # インスタンス生成時に呼び出されるコンストラクタ
-... def __init__(self, name, age):
-... print(f"新しい犬のインスタンスが作られます!")
-... # self.変数名 の形でインスタンス変数を定義
-... self.name = name
-... self.age = age
-...
->>> # インスタンス化する際、__init__のself以外の引数を渡す
->>> my_dog = Dog("ポチ", 3)
-新しい犬のインスタンスが作られます!
->>>
->>> # インスタンス変数にアクセス
->>> my_dog.name
-'ポチ'
->>> my_dog.age
-3
+Pythonのクラスでは、`__init__`という名前の特殊なメソッドがコンストラクタの役割を果たします。このメソッドは、クラスがインスタンス化される際に自動的に呼び出されます。
+
+メソッドの最初の引数には`self`を書くのが慣習です。これはインスタンス自身への参照であり、JavaやC++の`this`に相当します。ただし、Pythonでは`self`を明示的に引数として記述する必要があります。
+
+```python:dog2.py
+class Dog:
+ # インスタンス生成時に呼び出されるコンストラクタ
+ def __init__(self, name, breed):
+ print(f"{name}という名前の犬が作成されました。")
+ # self.変数名 の形でインスタンス変数を定義
+ self.name = name
+ self.breed = breed
+
+# インスタンス化する際に__init__のself以外の引数を渡すと、
+# `__init__`メソッドが `self`に`my_dog`インスタンス、`name`に`"ポチ"`、`breed`に`"柴犬"`を受け取って実行される
+my_dog = Dog("ポチ", "柴犬")
+
+# インスタンス変数にアクセス
+print(f"名前: {my_dog.name}")
+print(f"犬種: {my_dog.breed}")
+```
+
+```python-exec:dog2.py
+ポチという名前の犬が作成されました。
+名前: ポチ
+犬種: 柴犬
```
## インスタンス変数とクラス変数
-Pythonのクラスには2種類の変数があります。
+Pythonのクラスには、2種類の変数があります。
* **インスタンス変数**: `self.変数名`のように`__init__`内などで定義され、**各インスタンスに固有**の値を持ちます。上の例の`name`や`age`がこれにあたります。
* **クラス変数**: クラス定義の直下に書かれ、そのクラスから作られた**全てのインスタンスで共有**されます。
-
-
-```python
->>> class Cat:
-... # このクラスから作られるインスタンス全てで共有されるクラス変数
-... species = "ネコ科"
-...
-... def __init__(self, name):
-... # このインスタンス固有のインスタンス変数
-... self.name = name
-...
->>> tama = Cat("タマ")
->>> mike = Cat("ミケ")
->>>
->>> # インスタンス変数はそれぞれ異なる
->>> tama.name
-'タマ'
->>> mike.name
-'ミケ'
->>>
->>> # クラス変数は共有されている
->>> tama.species
-'ネコ科'
->>> mike.species
-'ネコ科'
->>>
->>> # クラス変数を変更すると、全てのインスタンスに影響する
->>> Cat.species = "イヌ科?"
->>> tama.species
-'イヌ科?'
->>> mike.species
-'イヌ科?'
+```python:dog3.py
+class Dog:
+ # このクラスから作られるインスタンス全てで共有されるクラス変数
+ species = "イヌ科"
+
+ def __init__(self, name):
+ # このインスタンス固有のインスタンス変数
+ self.name = name
+
+dog1 = Dog("ポチ")
+dog2 = Dog("ハチ")
+
+# インスタンス変数へのアクセス
+print(f"{dog1.name}も{dog2.name}も、")
+
+# クラス変数へのアクセス (インスタンス経由でもクラス経由でも可能)
+print(f"種は同じく {dog1.species} です。")
+print(f"Dogクラスの種は {Dog.species} です。")
+
+# クラス変数を変更すると、全てのインスタンスに影響が及ぶ
+Dog.species = "ネコ科"
+print(f"{dog1.name}の種は {dog1.species}")
+print(f"{dog2.name}の種は {dog2.species}")
```
-## メソッドの定義
+```python-exec:dog3.py
+ポチもハチも、
+種は同じく イヌ科 です。
+Dogクラスの種は イヌ科 です。
+ポチの種は ネコ科
+ハチの種は ネコ科
+```
-クラス内で定義された関数を**メソッド**と呼びます。メソッドは、そのクラスのインスタンスが持つべき振る舞いを定義します。
+## メソッドの定義
+クラス内で定義される関数をメソッドと呼びます。インスタンスのデータ(インスタンス変数)を操作するために使用します。
メソッドを定義する際も、最初の引数には必ず`self`を指定する必要があります。これにより、メソッド内から`self`を通じてインスタンス変数にアクセスできます。
-```python
->>> class Dog:
-... def __init__(self, name):
-... self.name = name
-...
-... # barkというメソッドを定義
-... # selfを介してインスタンス変数nameにアクセスする
-... def bark(self):
-... return f"{self.name}: ワン!"
-...
->>> pochi = Dog("ポチ")
->>>
->>> # メソッドを呼び出す
->>> pochi.bark()
-'ポチ: ワン!'
+```python:dog4.py
+class Dog:
+ def __init__(self, name):
+ self.name = name
+
+ # barkというメソッドを定義
+ # selfを介してインスタンス変数nameにアクセスする
+ def bark(self):
+ return f"{self.name}: ワン!"
+
+my_dog = Dog("ポチ")
+print(my_dog.bark()) # メソッドの呼び出し
+```
+
+```python-exec:dog4.py
+ポチ: ワン!
+```
+
+### クラスメンバーの型アノテーション
+
+型安全性を高めるために、クラス変数やインスタンス変数にも型アノテーション(型ヒント)を付けることができます。
+
+ * **クラス変数**: `変数名: 型 = 値` のように記述します。
+ * **インスタンス変数**: `__init__`内で `self.変数名: 型 = 値` のように記述するか、クラス直下で `変数名: 型` と宣言だけしておくこともできます。
+
+```python:dog5.py
+class Dog:
+ # クラス変数の型アノテーション
+ species: str = "イヌ科"
+
+ # インスタンス変数の型を宣言
+ name: str
+ age: int
+
+ def __init__(self, name: str, age: int):
+ self.name = name
+ self.age = age
+
+ # メソッドの戻り値の型アノテーション
+ def bark(self) -> str:
+ return f"{self.name}: ワン!"
+
+my_dog = Dog("ポチ", 3)
+```
+
+```python-exec:dog5.py
```
## 継承
-**継承**は、あるクラス(親クラス、スーパークラス)の性質を、新しいクラス(子クラス、サブクラス)が引き継ぐ仕組みです。コードの再利用性を高めるOOPの重要な概念です。
-
-Pythonでは`class 子クラス名(親クラス名):`という構文で継承を表現します。
-
-子クラスで親クラスのメソッドを上書き(**オーバーライド**)したり、`super()`関数を使って親クラスのメソッドを呼び出したりすることもできます。特に、子クラスの`__init__`で親クラスの`__init__`を呼び出すのは一般的なパターンです。
-
-```python
->>> # 親クラス
->>> class Animal:
-... def __init__(self, name):
-... print("Animalの__init__が呼ばれました")
-... self.name = name
-...
-... def eat(self):
-... return f"{self.name}は食事中です。"
-...
->>> # Animalクラスを継承した子クラス
->>> class Dog(Animal):
-... def __init__(self, name, breed):
-... print("Dogの__init__が呼ばれました")
-... # super()で親クラスの__init__を呼び出し、nameを初期化
-... super().__init__(name)
-... self.breed = breed # Dogクラス独自のインスタンス変数を追加
-...
-... # Dogクラス独自のメソッド
-... def bark(self):
-... return "ワン!"
-...
->>> # Dogクラスをインスタンス化
->>> my_dog = Dog("ポチ", "柴犬")
+あるクラスの機能を引き継いだ新しいクラスを作成することを継承と呼びます。Pythonでは、クラス定義の際に`()`内に親クラス(基底クラス)を指定することで継承を行います。
+
+子クラス(派生クラス)は親クラスのメソッドや変数を全て利用でき、必要に応じて上書き(オーバーライド)することも可能です。親クラスのメソッドを呼び出したい場合は`super()`を使います。
+
+```python:dog6.py
+# 親クラス
+class Animal:
+ def __init__(self, name: str):
+ print("Animalの__init__が呼ばれました")
+ self.name = name
+
+ def eat(self) -> str:
+ return f"{self.name}は食事中です。"
+
+ def speak(self) -> str:
+ return "..."
+
+# Animalクラスを継承した子クラス
+class Dog(Animal):
+ def __init__(self, name: str, breed: str):
+ print("Dogの__init__が呼ばれました")
+ # super()で親クラスの__init__を呼び出し、nameを初期化
+ super().__init__(name)
+ self.breed = breed # Dogクラス独自のインスタンス変数を追加
+
+ # 親のメソッドをオーバーライド
+ def speak(self) -> str:
+ return f"{self.name}: ワン!"
+
+
+dog = Dog("ポチ", "柴犬")
+
+# 親クラスのメソッドも使える
+print(dog.eat())
+# オーバーライドしたメソッドが呼ばれる
+print(dog.speak())
+```
+
+```python-exec:dog6.py
Dogの__init__が呼ばれました
Animalの__init__が呼ばれました
->>>
->>> # 親クラスのメソッドも使える
->>> my_dog.eat()
-'ポチは食事中です。'
->>>
->>> # もちろん子クラス独自のメソッドも使える
->>> my_dog.bark()
-'ワン!'
->>>
->>> # 親クラスと子クラス両方で初期化されたインスタンス変数を持つ
->>> my_dog.name
-'ポチ'
->>> my_dog.breed
-'柴犬'
+ポチは食事中です。
+ポチ: ワン!
```
## 基本的なマジックメソッド (`__str__`, `__repr__`)
-`__init__`のように、`__`(ダブルアンダースコア)で囲まれたメソッドは**マジックメソッド**(または特殊メソッド)と呼ばれます。これらは、特定の操作(`print()`での表示や演算子の使用など)に対応して、Pythonが裏側で自動的に呼び出す特別なメソッドです。
-
-特に重要なマジックメソッドとして`__str__`と`__repr__`があります。
-
- * `__str__`: `print()`関数や`str()`でオブジェクトが呼ばれたときに使われます。**人間が読みやすい**、非公式な文字列表現を返すことを目的とします。
- * `__repr__`: `repr()`関数やREPL環境で変数を評価したときに使われます。**開発者向け**で、そのオブジェクトを再生成できるような、公式で曖昧さのない文字列表現を返すことが理想です (`eval(repr(obj)) == obj`となるのが望ましい)。
-
-
-
-```python
->>> class Person:
-... def __init__(self, name, age):
-... self.name = name
-... self.age = age
-...
-... # print()で表示したときの振る舞いを定義
-... def __str__(self):
-... return f"名前: {self.name}, 年齢: {self.age}"
-...
-... # REPLでの評価やrepr()での振る舞いを定義
-... def __repr__(self):
-... return f"Person(name='{self.name}', age={self.age})"
-...
->>> bob = Person("ボブ", 30)
->>>
->>> # print()は__str__を呼び出す
->>> print(bob)
-名前: ボブ, 年齢: 30
->>>
->>> # str()も__str__を呼び出す
->>> str(bob)
-'名前: ボブ, 年齢: 30'
->>>
->>> # REPLで変数をそのまま評価すると__repr__が呼び出される
->>> bob
-Person(name='ボブ', age=30)
->>>
->>> # repr()も__repr__を呼び出す
->>> repr(bob)
-"Person(name='ボブ', age=30)"
-```
-
-もし`__str__`が定義されていない場合、`print()`は代わりに`__repr__`を呼び出します。両方を定義しておくことで、オブジェクトの使われ方に応じた適切な文字列表現を提供できます。
\ No newline at end of file
+`__init__`のように、アンダースコア2つで囲まれた特殊なメソッドを**マジックメソッド**(または**ダンダーメソッド**)と呼びます。これらを定義することで、Pythonの組み込み関数の挙動をカスタマイズできます。
+
+ * `__str__(self)`
+ * `print()`関数や`str()`でオブジェクトを文字列に変換する際に呼び出されます。
+ * 目的は、**人間にとって読みやすい**、非公式な文字列表現を返すことです。
+ * `__repr__(self)`
+ * `repr()`関数で呼び出されるほか、`__str__`が定義されていない場合の`print()`や、インタラクティブシェルでオブジェクトを評価した際に使われます。
+ * 目的は、**曖昧さのない**、公式な文字列表現を返すことです。理想的には、その文字列を評価すると同じオブジェクトを再作成できるような表現(例: `MyClass(arg1=1, arg2='B')`)が望ましいです。
+
+```python:dog7.py
+class Dog:
+ def __init__(self, name, age):
+ self.name = name
+ self.age = age
+
+ # print()で表示したときの振る舞いを定義
+ def __str__(self):
+ return f"名前: {self.name}, 年齢: {self.age}"
+
+ # REPLでの評価やrepr()での振る舞いを定義
+ def __repr__(self):
+ return f"Dog(name='{self.name}', age={self.age})"
+
+dog = Dog("ポチ", 3)
+
+# print()は__str__を呼び出す
+print(dog)
+
+# str()も__str__を呼び出す
+print(str(dog))
+
+# repr()は__repr__を呼び出す
+print(repr(dog))
+
+# REPLやJupyter Notebookなどで変数をそのまま評価すると__repr__が表示される
+# >>> dog
+# Dog(name='ポチ', age=3)
+```
+
+```python-exec:dog7.py
+名前: ポチ, 年齢: 3
+名前: ポチ, 年齢: 3
+Dog(name='ポチ', age=3)
+```
+
+## この章のまとめ
+
+ * **クラス定義**: `class ClassName:` で定義する。
+ * **コンストラクタ**: `__init__(self, ...)` メソッドで、インスタンス化の際に初期化処理を行う。
+ * **`self`**: インスタンス自身を指す参照。メソッドの第一引数として必ず記述する。
+ * **変数**: インスタンスごとに持つ**インスタンス変数**と、全インスタンスで共有する**クラス変数**がある。
+ * **継承**: `class Child(Parent):` でクラスの機能を引き継ぐ。親のメソッドは`super()`で呼び出せる。
+ * **マジックメソッド**: `__str__`や`__repr__`などを定義することで、オブジェクトの振る舞いをカスタマイズできる。
+
+## この章のまとめ
+
+この章では、Pythonにおけるオブジェクト指向プログラミングの基本を学びました。
+
+ * **クラスとインスタンス**: `class`キーワードでクラスという「設計図」を定義し、`クラス名()`でインスタンスという「実体」を作成します。
+ * **`__init__`と`self`**: `__init__`はインスタンス化の際に呼ばれるコンストラクタです。第一引数の`self`はインスタンス自身を指し、`self.変数名`の形でインスタンスごとにユニークな**インスタンス変数**を定義します。
+ * **クラス変数**: クラス直下に定義され、全てのインスタンスで共有される変数です。
+ * **メソッド**: クラス内で定義される関数で、インスタンスの振る舞いを表します。メソッドの第一引数も必ず`self`です。
+ * **継承**: `class 子クラス(親クラス):`と書くことで、親クラスの機能を引き継いだ新しいクラスを作成できます。`super()`を使うことで、親クラスのメソッドを呼び出せます。
+ * **マジックメソッド**: `__str__`や`__repr__`のように`__`で囲まれた特殊なメソッドで、`print()`などの組み込み関数の挙動をカスタマイズできます。
+
+PythonのOOPは、JavaやC++に比べてシンプルで直感的な構文が特徴です。しかし、その裏側にある「すべてがオブジェクトである」という思想は一貫しており、非常に強力なプログラミングパラダイムです。
+
+### 練習問題1: `Book`クラスの作成
+
+書籍の情報を管理する`Book`クラスを作成してください。
+
+**要件:**
+
+1. インスタンス化する際に、`title`(タイトル)と`author`(著者)を引数で受け取る。
+2. `info()`というメソッドを持ち、呼び出すと`「{タイトル}」- {著者}`という形式の文字列を返す。
+3. `print()`でインスタンスを直接表示した際に、`info()`メソッドと同じ文字列が表示されるようにする。
+
+```python:practice6_1.py
+class Book:
+
+
+if __name__ == "__main__":
+ harry_potter = Book("ハリー・ポッターと賢者の石", "J.K. ローリング")
+ print(harry_potter.info())
+ print(harry_potter)
+```
+
+```python-exec:practice6_1.py
+「ハリー・ポッターと賢者の石」- J.K. ローリング
+「ハリー・ポッターと賢者の石」- J.K. ローリング
+```
+
+#### 練習問題2: 継承を使った`EBook`クラスの作成
+
+問題1で作成した`Book`クラスを継承して、電子書籍を表す`EBook`クラスを作成してください。
+
+**要件:**
+
+1. `Book`クラスを継承する。
+2. インスタンス化の際に、`title`、`author`に加えて`file_size`(ファイルサイズ、MB単位)も引数で受け取る。
+3. `info()`メソッドを**オーバーライド**し、呼び出すと`「{タイトル}」- {著者} (ファイルサイズ: {file_size}MB)`という形式の文字列を返すように変更する。
+
+```python:practice6_2.py
+from practice6_1 import Book
+
+class EBook(Book):
+
+
+if __name__ == "__main__":
+ ebook_version = EBook("Python実践入門", "掌田 津耶乃", 24)
+ print(ebook_version.info())
+```
+
+```python-exec:practice6_2.py
+「Python実践入門」- 掌田 津耶乃 (ファイルサイズ: 24MB)
+```
diff --git a/public/docs/python-7.md b/public/docs/python-7.md
index 01a280c..5636bbe 100644
--- a/public/docs/python-7.md
+++ b/public/docs/python-7.md
@@ -15,9 +15,7 @@ Pythonでファイルを操作するには、まず組み込み関数の **`open
* `'+'` を付けると読み書き両用になります(例: `'r+'`, `'w+'`)。
* `'b'` を付けるとバイナリモードになります(例: `'rb'`, `'wb'`)。
-
-
-```python
+```python-repl
>>> # 'w' モードでファイルを開く(または新規作成する)
>>> f = open('spam.txt', 'w', encoding='utf-8')
>>> f
@@ -37,7 +35,7 @@ Pythonでファイルを操作するには、まず組み込み関数の **`open
**`write()`** メソッドは、文字列をファイルに書き込みます。このメソッドは書き込んだ文字数を返します。
-```python
+```python-repl
>>> f = open('test.txt', 'w', encoding='utf-8')
>>> f.write('こんにちは、世界!\n')
9
@@ -48,6 +46,11 @@ Pythonでファイルを操作するには、まず組み込み関数の **`open
`write()` は自動的には改行しないため、必要であれば自分で改行コード `\n` を追加します。
+```text-readonly:test.txt
+こんにちは、世界!
+これは2行目です。
+```
+
### 読み込み
ファイルからデータを読み込むには、いくつかの方法があります。
@@ -56,9 +59,7 @@ Pythonでファイルを操作するには、まず組み込み関数の **`open
* **`readline()`**: ファイルから1行だけを読み込み、文字列として返します。
* **`readlines()`**: ファイルのすべての行を読み込み、各行を要素とするリストで返します。
-
-
-```python
+```python-repl
>>> # 先ほど書き込んだファイルを読み込む
>>> f = open('test.txt', 'r', encoding='utf-8')
>>> content = f.read()
@@ -86,7 +87,7 @@ Pythonでファイルを操作するには、まず組み込み関数の **`open
**`with`** 文のブロックを抜けると、ファイルオブジェクトは自動的に `close()` されます。エラーが発生した場合でも同様です。これは「コンテキストマネージャ」という仕組みによって実現されており、ファイル操作の標準的な方法です。
-```python
+```python-repl
>>> # with文を使った書き込み
>>> with open('spam.txt', 'w', encoding='utf-8') as f:
... f.write('withブロックを使っています。\n')
@@ -108,6 +109,10 @@ withブロックを使っています。
このように、`with` 文を使えば `close()` の呼び出しを忘れる心配がなく、コードもすっきりします。今後は常に `with` 文を使ってファイルを扱うようにしましょう。
+```text-readonly:spam.txt
+withブロックを使っています。
+ブロックを抜けると自動で閉じられます。
+```
## `json`モジュールを使ったJSONの操作
@@ -118,7 +123,7 @@ withブロックを使っています。
-```python
+```python-repl
>>> import json
>>> # 書き込むデータ(Pythonの辞書)
@@ -147,6 +152,17 @@ withブロックを使っています。
`json.dump()` の `indent=4` は、人間が読みやすいように4スペースのインデントを付けて出力するオプションです。`ensure_ascii=False` は、日本語などの非ASCII文字をそのままの文字で出力するために指定します。
+```json-readonly:user.json
+{
+ "name": "Taro Yamada",
+ "age": 30,
+ "is_student": false,
+ "courses": [
+ "Python",
+ "Machine Learning"
+ ]
+}
+```
## `csv`モジュールを使ったCSVの操作
@@ -156,7 +172,7 @@ withブロックを使っています。
**`csv.writer()`** を使ってライターオブジェクトを作成し、**`writerow()`** (1行) や **`writerows()`** (複数行) メソッドでデータを書き込みます。
-```python
+```python-repl
>>> import csv
>>> # 書き込むデータ(リストのリスト)
@@ -174,11 +190,18 @@ withブロックを使っています。
...
```
+```csv-readonly:scores.csv
+ID,Name,Score
+1,Alice,95
+2,Bob,88
+3,Charlie,76
+```
+
### CSVファイルの読み込み
**`csv.reader()`** を使ってリーダーオブジェクトを作成します。このオブジェクトをループで回すことで、1行ずつリストとしてデータを取得できます。
-```python
+```python-repl
>>> import csv
>>> with open('scores.csv', 'r', newline='', encoding='-utf-8') as f:
@@ -193,4 +216,62 @@ withブロックを使っています。
['3', 'Charlie', '76']
```
-注意点として、`csv`モジュールはすべてのデータを文字列として読み込みます。数値として扱いたい場合は、自分で `int()` や `float()` を使って型変換する必要があります。
\ No newline at end of file
+注意点として、`csv`モジュールはすべてのデータを文字列として読み込みます。数値として扱いたい場合は、自分で `int()` や `float()` を使って型変換する必要があります。
+
+
+## この章のまとめ
+
+この章では、Pythonを使った基本的なファイルの入出力について学びました。
+
+ - **`open()` 関数**: ファイルを開き、ファイルオブジェクトを取得するための基本です。モード (`'r'`, `'w'`, `'a'`) とエンコーディング (`'utf-8'`) の指定が重要です。
+ - **ファイルメソッド**: **`.read()`**, **`.readline()`**, **`.write()`** といったメソッドを使って、ファイルの内容を操作します。
+ - **`with` 文**: ファイルを自動的に閉じるための最も安全で推奨される方法です。**コンテキストマネージャ**の仕組みにより、後片付けが確実に行われます。
+ - **`json` モジュール**: Pythonの辞書やリストを、広く使われているデータ形式であるJSONとして読み書きするために使用します。**`json.dump()`** と **`json.load()`** が中心的な関数です。
+ - **`csv` モジュール**: カンマ区切りの表形式データを扱うためのモジュールです。**`csv.writer()`** と **`csv.reader()`** を使って、行単位での読み書きを簡単に行えます。
+
+ファイル操作は、プログラムの設定を保存したり、処理結果を記録したり、他のシステムとデータをやり取りしたりするなど、あらゆるアプリケーションで必要となる基本的なスキルです。
+
+### 練習問題1: ユーザー情報の書き出しと読み込み
+
+1. 以下の情報を持つユーザーのデータを、Pythonの辞書として作成してください。
+ * `id`: 101
+ * `name`: "Sato Kenji"
+ * `email`: "kenji.sato@example.com"
+2. この辞書を、`with` 文と `json` モジュールを使って `user_profile.json` という名前のファイルに書き出してください。その際、人間が読みやすいようにインデントを付けてください。
+3. 書き出した `user_profile.json` ファイルを読み込み、内容をコンソールに表示してください。
+
+```python:practice7_1.py
+```
+
+```python-exec:practice7_1.py
+(出力例) {'id': 101, 'name': 'Sato Kenji', 'email': 'kenji.sato@example.com'}
+```
+
+```json-readonly:user_profile.json
+```
+
+### 練習問題2: 売上データのCSV集計
+
+あなたは店舗の売上データ(CSV形式)を処理する必要があります。以下の手順でプログラムを作成してください。
+
+1. `sales.csv` ファイルを読み込みモードで開きます。
+2. `csv.reader` を使ってデータを1行ずつ読み込み、ヘッダー行(1行目)は無視してください。
+3. 各商品の「価格」と「数量」を掛け合わせ、その行の売上金額を計算します。
+4. すべての商品の合計売上金額を計算し、最後に「合計売上: XXXX円」という形式でコンソールに出力してください。
+
+**ヒント:** `csv` モジュールで読み込んだ値はすべて文字列型です。計算する前に `int()` を使って整数型に変換する必要があります。
+
+```csv-readonly:sales.csv
+商品,価格,数量
+リンゴ,120,3
+バナナ,80,5
+オレンジ,150,2
+ブドウ,300,1
+```
+
+```python:practice7_2.py
+```
+
+```python-exec:practice7_2.py
+(出力例) 合計売上: 1360円
+```
diff --git a/public/docs/python-8.md b/public/docs/python-8.md
index c547e55..e1bfba4 100644
--- a/public/docs/python-8.md
+++ b/public/docs/python-8.md
@@ -8,7 +8,7 @@
例えば、`0` で割り算をすると `ZeroDivisionError` という例外が発生します。
-```python
+```python-repl
>>> 10 / 0
Traceback (most recent call last):
File "", line 1, in
@@ -17,7 +17,7 @@ ZeroDivisionError: division by zero
このエラーを `try...except` で捕捉してみましょう。
-```python
+```python-repl
>>> try:
... result = 10 / 0
... except ZeroDivisionError:
@@ -38,7 +38,7 @@ ZeroDivisionError: division by zero
エラーの種類ごとに異なる処理を行いたい場合に適しています。
-```python
+```python-repl
>>> def calculate(a, b):
... try:
... a = int(a)
@@ -62,7 +62,7 @@ ZeroDivisionError: division by zero
複数の例外に対して同じ処理を行いたい場合に便利です。
-```python
+```python-repl
>>> def calculate_v2(a, b):
... try:
... a = int(a)
@@ -86,7 +86,7 @@ ZeroDivisionError: division by zero
例えば、負の値を受け付けない関数を考えてみましょう。
-```python
+```python-repl
>>> def process_positive_number(num):
... if num < 0:
... raise ValueError("負の値は処理できません。")
@@ -112,7 +112,7 @@ ValueError: 負の値は処理できません。
すべての節を使った例を見てみましょう。
-```python
+```python-repl
>>> def divider(a, b):
... print(f"--- {a} / {b} の計算を開始します ---")
... try:
@@ -144,4 +144,76 @@ ValueError: 負の値は処理できません。
* 成功ケースでは `try` -\> `else` -\> `finally` の順に実行されます。
* 失敗ケースでは `try` -\> `except` -\> `finally` の順に実行されます。
-`finally` 節は、`try` ブロック内で `return` が実行される場合でも、その `return` の直前に実行されることが保証されています。これにより、リソースの解放漏れなどを防ぐことができます。
\ No newline at end of file
+`finally` 節は、`try` ブロック内で `return` が実行される場合でも、その `return` の直前に実行されることが保証されています。これにより、リソースの解放漏れなどを防ぐことができます。
+
+## この章のまとめ
+
+この章では、Pythonにおけるエラー処理の基本を学びました。重要なポイントを振り返りましょう。
+
+ * **例外**: Pythonでは、エラーは「例外」オブジェクトとして扱われます。例外が発生すると、プログラムの実行は中断されます。
+ * `try...except`: 例外が発生する可能性のあるコードを `try` ブロックで囲み、`except` ブロックで捕捉することで、プログラムのクラッシュを防ぎ、エラーに応じた処理を実行できます。
+ * **複数の例外処理**: `except` ブロックを複数記述したり、タプルでまとめたりすることで、さまざまな種類のエラーに柔軟に対応できます。
+ * `raise`: 特定の条件で意図的に例外を発生させ、プログラムに異常な状態を通知します。
+ * `else` と `finally`: `try` ブロックが成功した場合の処理を `else` に、成功・失敗にかかわらず必ず実行したい後片付け処理を `finally` に記述することで、より堅牢なコードを書くことができます。
+
+例外処理をマスターすることは、予期せぬ入力や状況に強い、安定したプログラムを作成するための重要なステップです。
+
+### 練習問題1: 安全なリスト要素の取得
+
+リストとインデックスを受け取り、そのインデックスに対応する要素を返す `safe_get(my_list, index)` という関数を作成してください。
+
+**要件:**
+
+1. インデックスがリストの範囲外の場合 (`IndexError`)、「指定されたインデックスは範囲外です。」と表示してください。
+2. インデックスが整数でない場合 (`TypeError`)、「インデックスは整数で指定してください。」と表示してください。
+3. 正常に要素を取得できた場合は、その要素を返してください。
+
+```python:practice8_1.py
+def safe_get(my_list, index):
+
+
+data = ['apple', 'banana', 'cherry']
+print(safe_get(data, 1))
+print(safe_get(data, 3))
+print(safe_get(data, 'zero'))
+```
+
+```python-exec:practice8_1.py
+(出力例)
+banana
+指定されたインデックスは範囲外です。
+インデックスは整数で指定してください。
+```
+
+### 練習問題2: ユーザー年齢の検証
+
+ユーザーの年齢を入力として受け取り、18歳以上であれば「あなたは成人です。」と表示する `check_age(age_str)` という関数を作成してください。
+
+**要件:**
+
+1. 関数内部で、受け取った文字列を整数に変換してください。変換できない場合 (`ValueError`) は、`ValueError` を `raise` して、「有効な数値を入力してください。」というメッセージを伝えてください。
+2. 変換した数値が負の値である場合、`ValueError` を `raise` して、「年齢に負の値は指定できません。」というメッセージを伝えてください。
+3. 年齢が0歳から17歳までの場合は、「あなたは未成年です。」と表示してください。
+4. 関数の呼び出し側で、`raise` された例外も捕捉できるようにしてください。
+
+```python:practice8_2.py
+def check_age(age_str):
+
+
+# 正常ケース
+print(check_age("20"))
+print(check_age("15"))
+
+# 例外ケース
+print(check_age("abc"))
+print(check_age("-5"))
+```
+
+```python-exec:practice8_2.py
+(出力例)
+あなたは成人です。
+あなたは未成年です。
+Traceback (most recent call last):
+ ...
+ValueError: 有効な数値を入力してください。
+```
diff --git a/public/docs/python-9.md b/public/docs/python-9.md
index e21aeba..a31652c 100644
--- a/public/docs/python-9.md
+++ b/public/docs/python-9.md
@@ -13,7 +13,7 @@ Pythonの`for`ループは非常にシンプルで強力ですが、その裏側
REPLで動きを見てみましょう。`iter()`関数でイテレータを取得し、`next()`関数で要素を取り出します。
-```python
+```python-repl
>>> my_list = [1, 2, 3]
>>> my_iterator = iter(my_list)
>>> type(my_iterator)
@@ -45,7 +45,7 @@ StopIteration
フィボナッチ数列を生成するジェネレータの例を見てみましょう。
-```python
+```python-repl
>>> def fib_generator(n):
... a, b = 0, 1
... count = 0
@@ -86,7 +86,7 @@ StopIteration
リスト内包表記はリストオブジェクトを生成するため、要素数が多いとメモリを大量に消費します。一方、ジェネレータ式はジェネレータオブジェクトを返すため、遅延評価(必要になるまで計算しない)が行われ、メモリ使用量を抑えられます。
-```python
+```python-repl
# リスト内包表記
>>> list_comp = [i * 2 for i in range(5)]
>>> list_comp
@@ -127,7 +127,7 @@ StopIteration
関数の実行前後にメッセージを表示する簡単なデコレータを見てみましょう。
-```python
+```python-repl
>>> def my_decorator(func):
... def wrapper():
... print("--- 処理を開始します ---")
@@ -150,7 +150,7 @@ StopIteration
この書き方をより簡単にするための構文が `@`(アットマーク)、シンタックスシュガーです。
-```python
+```python-repl
>>> @my_decorator
... def say_goodbye():
... print("さようなら!")
@@ -164,4 +164,68 @@ StopIteration
`@my_decorator` は、`say_goodbye = my_decorator(say_goodbye)` と同じ意味になります。こちらのほうが直感的で、Pythonのコードで広く使われています。
-ジェネレータとデコレータは、最初は少し複雑に感じるかもしれませんが、使いこなせばよりクリーンで効率的なPythonコードを書くための強力な武器となります。ぜひ積極的に活用してみてください。
\ No newline at end of file
+ジェネレータとデコレータは、最初は少し複雑に感じるかもしれませんが、使いこなせばよりクリーンで効率的なPythonコードを書くための強力な武器となります。ぜひ積極的に活用してみてください。
+
+はい、承知いたしました。先に作成したチュートリアルの末尾に追加する「この章のまとめ」と「練習問題」を作成します。
+
+## この章のまとめ
+
+この章では、Pythonプログラミングをさらに高いレベルへ引き上げるための2つの強力な概念を学びました。
+
+ * **ジェネレータ**: `yield`キーワードを使うことで、メモリ効率に優れたイテレータを簡単に作成できることを学びました。ジェネレータ関数やジェネレータ式を使うことで、巨大なデータストリームや無限シーケンスを、必要な分だけ計算しながら扱うことができます。これは、パフォーマンスが重要なアプリケーションにおいて不可欠なテクニックです。
+
+ * **デコレータ**: `@`シンタックスを用いることで、既存の関数のソースコードを変更することなく、機能を追加・変更できることを学びました。デコレータは、ロギング、実行時間計測、アクセス制御といった横断的な関心事を分離し、コードの再利用性を高め、DRY (Don't Repeat Yourself) の原則を維持するのに役立ちます。
+
+これらの機能を使いこなすことは、単に高度な文法を覚えるだけでなく、Pythonの設計思想を理解し、より「Pythonらしい(Pythonic)」コードを書くための重要なステップです。
+
+### 練習問題1: カウントダウンジェネレータ
+
+`countdown(start)` というジェネレータ関数を作成してください。この関数は、引数で与えられた `start` の数値から1まで、1ずつ減っていく数値を順番に `yield` します。例えば `countdown(3)` は、`3`, `2`, `1` の順に値を生成します。
+
+```python:practice9_1.py
+def countdown(start):
+
+
+# 動作確認
+cd_gen = countdown(5)
+for i in cd_gen:
+ print(i)
+```
+
+```python-exec:practice9_1.py
+(出力例)
+5
+4
+3
+2
+1
+```
+
+### 問題2: 実行時間計測デコレータ
+
+関数の実行時間を計測し、"実行時間: X.XXXX秒" のように表示するデコレータ `@measure_time` を作成してください。このデコレータを、少し時間のかかる処理を行う関数に適用して、動作を確認してみましょう。
+
+**ヒント**: 時間の計測には `time` モジュールが使えます。処理の開始前と終了後で `time.time()` を呼び出し、その差分を計算します。
+
+```python:practice9_2.py
+import time
+
+def measure_time(func):
+
+
+# 動作確認用の時間のかかる関数
+@measure_time
+def slow_function(n):
+ print(f"{n}まで数えます...")
+ time.sleep(n) # n秒間処理を停止
+ print("完了!")
+
+slow_function(2)
+```
+
+```python-exec:practice9_2.py
+(出力例)
+2まで数えます...
+完了!
+実行時間: 2.0021秒
+```