From 92f94509a8a0ba6a5423f6b46d7ea7836871b5cb Mon Sep 17 00:00:00 2001 From: hvnsweeting Date: Tue, 15 Oct 2024 14:02:16 +0000 Subject: [PATCH] [ci skip] Automated deployment to GitHub Pages on 1729000936296 --- 2ty.html | 142 + 3c.html | 135 + CNAME | 1 + aoc2020-norvig.html | 110 + aoc2022.html | 123 + aoc2022e.html | 116 + archives.html | 3248 +++++ ast_intro.html | 261 + author/pymier0.html | 0 author/pymier010.html | 0 author/pymier02.html | 0 author/pymier03.html | 0 author/pymier04.html | 0 author/pymier05.html | 0 author/pymier06.html | 0 author/pymier07.html | 0 author/pymier08.html | 0 author/pymier09.html | 0 author/tung491.html | 0 authors.html | 0 base16.html | 195 + base64.html | 400 + base64impl.html | 173 + big_float.html | 148 + bool-methods.html | 129 + build.html | 153 + builtins.html | 188 + byt351.html | 208 + categories.html | 72 + category/features.html | 372 + category/features2.html | 103 + category/misc.html | 101 + category/news.html | 361 + category/news2.html | 366 + category/news3.html | 367 + category/news4.html | 363 + category/news5.html | 364 + category/news6.html | 358 + category/news7.html | 289 + category/pymivn.html | 366 + category/pymivn2.html | 162 + compile.html | 165 + compound.html | 228 + compound2.html | 223 + concurrent.html | 186 + counter.html | 187 + cv2.html | 131 + dataclass-leak.html | 239 + dh_requests.html | 148 + dict.get.html | 135 + dictkey.html | 160 + dictorder.html | 144 + dictvslist.html | 137 + dis1.html | 229 + disfor.html | 195 + disfun.html | 219 + disif.html | 156 + disimport.html | 187 + django-template.html | 205 + ellipsis.html | 137 + enum.html | 184 + equal.html | 154 + feeds/all.atom.xml | 5560 +++++++++ float_max.html | 137 + free.html | 118 + grayscale.html | 127 + hgdb-arch.html | 183 + hgdb.html | 214 + httpd.html | 128 + ieee2021.html | 114 + images/2ty.jpg | Bin 0 -> 30267 bytes images/alpha.png | Bin 0 -> 4026 bytes images/aoc22.png | Bin 0 -> 180711 bytes images/blue.png | Bin 0 -> 6770 bytes images/company_related.png | Bin 0 -> 40718 bytes images/compound2.png | Bin 0 -> 17052 bytes images/cuaitlongnhieu.png | Bin 0 -> 8287 bytes images/distribution_daily.png | Bin 0 -> 28272 bytes images/gray.jpg | Bin 0 -> 66289 bytes images/green.png | Bin 0 -> 6603 bytes images/hn2110.jpg | Bin 0 -> 88825 bytes images/hssv.png | Bin 0 -> 8936 bytes images/pygame_flag.png | Bin 0 -> 7926 bytes images/red.png | Bin 0 -> 5938 bytes images/tapthe.png | Bin 0 -> 34160 bytes images/topmoney.webp | Bin 0 -> 47248 bytes images/total_saoke_amount.png | Bin 0 -> 8322 bytes images/vscode.svg | 40 + images/vscode1.png | Bin 0 -> 2259 bytes images/vscode2.png | Bin 0 -> 2789 bytes images/vscode3.png | Bin 0 -> 3964 bytes images/vscode4.png | Bin 0 -> 2950 bytes images/vscode5.png | Bin 0 -> 5944 bytes images/vscode7.png | Bin 0 -> 3265 bytes images/z38queenpygame.png | Bin 0 -> 54729 bytes images/z3pygame8qv2.png | Bin 0 -> 57010 bytes images/z3speed.png | Bin 0 -> 21753 bytes index.html | 362 + index10.html | 162 + index2.html | 368 + index3.html | 361 + index4.html | 374 + index5.html | 364 + index6.html | 365 + index7.html | 374 + index8.html | 365 + index9.html | 344 + int2str.html | 135 + lambda_default.html | 126 + latebinding.html | 144 + leakvar.html | 150 + len.html | 115 + listarray.html | 130 + mime.html | 211 + mostwanted.html | 123 + mypystrict.html | 151 + new.html | 218 + overloading.html | 125 + pathlib.html | 148 + pe16.html | 128 + py36eol.html | 109 + py3utf8.html | 151 + pycurl.html | 218 + pygame-draw.html | 161 + pymi2205.html | 111 + pymi2207.html | 111 + pymi2210.html | 112 + pymi2302.html | 112 + pymi2305.html | 112 + pymi2307.html | 112 + pymi2403.html | 111 + pymihcm2101.html | 110 + pymihn2107.html | 112 + pymihn2110.html | 107 + range.html | 144 + rgba.html | 128 + saoke.html | 124 + slice_reverse.html | 341 + so.html | 159 + sparkpe1.html | 163 + sparkwc.html | 234 + speedtest.html | 121 + tag/1liner.html | 134 + tag/8-queens.html | 129 + tag/__builtins__.html | 98 + tag/adventofcode.html | 156 + tag/algorithm.html | 186 + tag/aoc.html | 156 + tag/array.html | 100 + tag/ast.html | 98 + tag/autobug.html | 100 + tag/backtracking.html | 100 + tag/base16.html | 99 + tag/base64.html | 128 + tag/beautifulcode.html | 100 + tag/best-practice.html | 102 + tag/bgr.html | 101 + tag/big-number.html | 101 + tag/big-o.html | 100 + tag/bigdata.html | 103 + tag/bigo.html | 101 + tag/binary.html | 100 + tag/bool.html | 99 + tag/boolean.html | 99 + tag/bug.html | 160 + tag/build.html | 99 + tag/busybox.html | 102 + tag/byte.html | 132 + tag/bytecode.html | 252 + tag/c.html | 128 + tag/calculus.html | 127 + tag/challenge.html | 100 + tag/changes.html | 102 + tag/class.html | 368 + tag/cli.html | 134 + tag/code.html | 99 + tag/collections.html | 100 + tag/compile.html | 161 + tag/compiler.html | 101 + tag/concurrent.html | 100 + tag/concurrentfutures.html | 100 + tag/contest.html | 127 + tag/control-flow.html | 132 + tag/count-words.html | 100 + tag/counter.html | 100 + tag/course.html | 341 + tag/cpu.html | 103 + tag/cpython.html | 130 + tag/crop.html | 101 + tag/crypto.html | 156 + tag/cryptography.html | 98 + tag/ctf.html | 100 + tag/curl.html | 101 + tag/cv2.html | 99 + tag/cve.html | 102 + tag/data-analysis.html | 103 + tag/dataclass.html | 101 + tag/debugger.html | 127 + tag/deep-reading.html | 99 + tag/default.html | 101 + tag/deque.html | 100 + tag/dict.html | 217 + tag/dictorder.html | 99 + tag/dis.html | 222 + tag/disassembly.html | 222 + tag/django.html | 100 + tag/dos.html | 102 + tag/draw.html | 99 + tag/dunder.html | 99 + tag/editor.html | 100 + tag/encoding.html | 128 + tag/eol.html | 98 + tag/exp.html | 102 + tag/extension.html | 127 + tag/factorize.html | 100 + tag/fast.html | 103 + tag/feature.html | 102 + tag/features.html | 255 + tag/finance.html | 159 + tag/fixbug.html | 131 + tag/float.html | 128 + tag/function.html | 129 + tag/gdb.html | 127 + tag/generator.html | 98 + tag/get.html | 101 + tag/grayscale.html | 101 + tag/hack.html | 132 + tag/hex.html | 160 + tag/http.html | 132 + tag/ieee.html | 97 + tag/ieee754.html | 98 + tag/ifelse.html | 101 + tag/image.html | 189 + tag/import.html | 100 + tag/insights.html | 99 + tag/int.html | 102 + tag/internal.html | 252 + tag/interview-question.html | 100 + tag/isqrt.html | 101 + tag/it.html | 103 + tag/itertools.html | 99 + tag/jinja2.html | 100 + tag/khai-giang.html | 98 + tag/khoa-hoc.html | 341 + tag/lambda.html | 98 + tag/life.html | 103 + tag/linux.html | 101 + tag/list.html | 130 + tag/macos.html | 101 + tag/market.html | 99 + tag/math.html | 189 + tag/methods.html | 99 + tag/mime.html | 101 + tag/mimetype.html | 101 + tag/mttq.html | 99 + tag/multiprocesses.html | 100 + tag/mypy.html | 100 + tag/news.html | 97 + tag/no1.html | 99 + tag/numpy.html | 101 + tag/opencv-python.html | 131 + tag/opencv.html | 189 + tag/operator.html | 99 + tag/or.html | 101 + tag/overloading.html | 99 + tag/pandas.html | 103 + tag/pe.html | 103 + tag/poor.html | 103 + tag/projecteuler.html | 103 + tag/projecteulernet.html | 100 + tag/pycurl.html | 101 + tag/pyfml.html | 341 + tag/pygame.html | 156 + tag/pymihn2110.html | 98 + tag/pymivn.html | 341 + tag/pyspark.html | 132 + tag/python.html | 360 + tag/python2.html | 193 + tag/python3.html | 103 + tag/question.html | 103 + tag/quiz.html | 98 + tag/range.html | 98 + tag/rank.html | 99 + tag/recursive.html | 99 + tag/repr.html | 101 + tag/requests.html | 129 + tag/rgb.html | 131 + tag/rgba.html | 101 + tag/rsa.html | 100 + tag/salary.html | 103 + tag/sao-ke.html | 99 + tag/sat.html | 187 + tag/security.html | 101 + tag/set.html | 100 + tag/shared-object.html | 100 + tag/slice.html | 97 + tag/smt.html | 187 + tag/so.html | 100 + tag/source.html | 128 + tag/spark.html | 132 + tag/speed.html | 130 + tag/sqrt.html | 101 + tag/ssl.html | 100 + tag/stackoverflow.html | 99 + tag/stdlib.html | 190 + tag/str.html | 164 + tag/sum.html | 103 + tag/survey.html | 99 + tag/template.html | 100 + tag/theorem-prover.html | 187 + tag/threading.html | 100 + tag/timeit.html | 133 + tag/tiobe.html | 99 + tag/tip.html | 99 + tag/totp.html | 98 + tag/type.html | 100 + tag/unicode.html | 103 + tag/utf-8.html | 103 + tag/van-mau.html | 100 + tag/variable.html | 102 + tag/version.html | 98 + tag/virtual-machine.html | 101 + tag/visualization.html | 99 + tag/vm.html | 102 + tag/vscode.html | 100 + tag/web-server.html | 102 + tag/z3.html | 302 + tag/zip.html | 126 + tag/zipapp.html | 98 + tag/zlib.html | 100 + tags.html | 256 + theme/css/data-ttf.css | 3590 ++++++ theme/css/data-woff.css | 1325 +++ theme/css/data-woff2.css | 1554 +++ theme/css/icons.data.png.css | 50 + theme/css/icons.data.svg.css | 50 + theme/css/icons.fallback.css | 50 + theme/css/main.css | 679 ++ theme/css/main.css.map | 1 + theme/css/png/facebook.png | Bin 0 -> 490 bytes theme/css/png/foursquare.png | Bin 0 -> 807 bytes theme/css/png/github.png | Bin 0 -> 869 bytes theme/css/png/goodreads.png | Bin 0 -> 1099 bytes theme/css/png/instagram.png | Bin 0 -> 1559 bytes theme/css/png/medium.png | Bin 0 -> 741 bytes theme/css/png/strava.png | Bin 0 -> 780 bytes theme/css/png/telegram.png | Bin 0 -> 969 bytes theme/css/png/twitter.png | Bin 0 -> 942 bytes theme/css/png/untappd.png | Bin 0 -> 875 bytes theme/css/sass/_mixins.sass | 41 + theme/css/sass/_pygment.sass | 126 + theme/css/sass/_reset.sass | 60 + theme/css/sass/main.sass | 346 + theme/fonts/lato-black-webfont.ttf | Bin 0 -> 57212 bytes theme/fonts/lato-black-webfont.woff | Bin 0 -> 31244 bytes theme/fonts/lato-black-webfont.woff2 | Bin 0 -> 24572 bytes theme/fonts/lato-light-webfont.ttf | Bin 0 -> 56700 bytes theme/fonts/lato-light-webfont.woff | Bin 0 -> 31028 bytes theme/fonts/lato-light-webfont.woff2 | Bin 0 -> 24212 bytes theme/fonts/lato-regular-webfont.ttf | Bin 0 -> 57540 bytes theme/fonts/lato-regular-webfont.woff | Bin 0 -> 31812 bytes theme/fonts/lato-regular-webfont.woff2 | Bin 0 -> 24920 bytes theme/js/moment.js | 14380 +++++++++++++++++++++++ tiobeno1.html | 112 + topmoney.html | 117 + totp.html | 135 + towerofhanoi.html | 164 + vscode.html | 125 + z38q.html | 205 + z38qgame.py | 63 + z38qgamev2.py | 103 + z3factor.html | 173 + z3grade3.html | 194 + z3ineq.html | 163 + z3p1.html | 135 + z3pygame.html | 180 + z3pygame8q.html | 163 + z3speed.html | 153 + zip.html | 147 + zipapp.html | 172 + zlib.html | 155 + 381 files changed, 76951 insertions(+) create mode 100644 2ty.html create mode 100644 3c.html create mode 100644 CNAME create mode 100644 aoc2020-norvig.html create mode 100644 aoc2022.html create mode 100644 aoc2022e.html create mode 100644 archives.html create mode 100644 ast_intro.html create mode 100644 author/pymier0.html create mode 100644 author/pymier010.html create mode 100644 author/pymier02.html create mode 100644 author/pymier03.html create mode 100644 author/pymier04.html create mode 100644 author/pymier05.html create mode 100644 author/pymier06.html create mode 100644 author/pymier07.html create mode 100644 author/pymier08.html create mode 100644 author/pymier09.html create mode 100644 author/tung491.html create mode 100644 authors.html create mode 100644 base16.html create mode 100644 base64.html create mode 100644 base64impl.html create mode 100644 big_float.html create mode 100644 bool-methods.html create mode 100644 build.html create mode 100644 builtins.html create mode 100644 byt351.html create mode 100644 categories.html create mode 100644 category/features.html create mode 100644 category/features2.html create mode 100644 category/misc.html create mode 100644 category/news.html create mode 100644 category/news2.html create mode 100644 category/news3.html create mode 100644 category/news4.html create mode 100644 category/news5.html create mode 100644 category/news6.html create mode 100644 category/news7.html create mode 100644 category/pymivn.html create mode 100644 category/pymivn2.html create mode 100644 compile.html create mode 100644 compound.html create mode 100644 compound2.html create mode 100644 concurrent.html create mode 100644 counter.html create mode 100644 cv2.html create mode 100644 dataclass-leak.html create mode 100644 dh_requests.html create mode 100644 dict.get.html create mode 100644 dictkey.html create mode 100644 dictorder.html create mode 100644 dictvslist.html create mode 100644 dis1.html create mode 100644 disfor.html create mode 100644 disfun.html create mode 100644 disif.html create mode 100644 disimport.html create mode 100644 django-template.html create mode 100644 ellipsis.html create mode 100644 enum.html create mode 100644 equal.html create mode 100644 feeds/all.atom.xml create mode 100644 float_max.html create mode 100644 free.html create mode 100644 grayscale.html create mode 100644 hgdb-arch.html create mode 100644 hgdb.html create mode 100644 httpd.html create mode 100644 ieee2021.html create mode 100644 images/2ty.jpg create mode 100644 images/alpha.png create mode 100644 images/aoc22.png create mode 100644 images/blue.png create mode 100644 images/company_related.png create mode 100644 images/compound2.png create mode 100644 images/cuaitlongnhieu.png create mode 100644 images/distribution_daily.png create mode 100644 images/gray.jpg create mode 100644 images/green.png create mode 100644 images/hn2110.jpg create mode 100644 images/hssv.png create mode 100644 images/pygame_flag.png create mode 100644 images/red.png create mode 100644 images/tapthe.png create mode 100644 images/topmoney.webp create mode 100644 images/total_saoke_amount.png create mode 100644 images/vscode.svg create mode 100644 images/vscode1.png create mode 100644 images/vscode2.png create mode 100644 images/vscode3.png create mode 100644 images/vscode4.png create mode 100644 images/vscode5.png create mode 100644 images/vscode7.png create mode 100644 images/z38queenpygame.png create mode 100644 images/z3pygame8qv2.png create mode 100644 images/z3speed.png create mode 100644 index.html create mode 100644 index10.html create mode 100644 index2.html create mode 100644 index3.html create mode 100644 index4.html create mode 100644 index5.html create mode 100644 index6.html create mode 100644 index7.html create mode 100644 index8.html create mode 100644 index9.html create mode 100644 int2str.html create mode 100644 lambda_default.html create mode 100644 latebinding.html create mode 100644 leakvar.html create mode 100644 len.html create mode 100644 listarray.html create mode 100644 mime.html create mode 100644 mostwanted.html create mode 100644 mypystrict.html create mode 100644 new.html create mode 100644 overloading.html create mode 100644 pathlib.html create mode 100644 pe16.html create mode 100644 py36eol.html create mode 100644 py3utf8.html create mode 100644 pycurl.html create mode 100644 pygame-draw.html create mode 100644 pymi2205.html create mode 100644 pymi2207.html create mode 100644 pymi2210.html create mode 100644 pymi2302.html create mode 100644 pymi2305.html create mode 100644 pymi2307.html create mode 100644 pymi2403.html create mode 100644 pymihcm2101.html create mode 100644 pymihn2107.html create mode 100644 pymihn2110.html create mode 100644 range.html create mode 100644 rgba.html create mode 100644 saoke.html create mode 100644 slice_reverse.html create mode 100644 so.html create mode 100644 sparkpe1.html create mode 100644 sparkwc.html create mode 100644 speedtest.html create mode 100644 tag/1liner.html create mode 100644 tag/8-queens.html create mode 100644 tag/__builtins__.html create mode 100644 tag/adventofcode.html create mode 100644 tag/algorithm.html create mode 100644 tag/aoc.html create mode 100644 tag/array.html create mode 100644 tag/ast.html create mode 100644 tag/autobug.html create mode 100644 tag/backtracking.html create mode 100644 tag/base16.html create mode 100644 tag/base64.html create mode 100644 tag/beautifulcode.html create mode 100644 tag/best-practice.html create mode 100644 tag/bgr.html create mode 100644 tag/big-number.html create mode 100644 tag/big-o.html create mode 100644 tag/bigdata.html create mode 100644 tag/bigo.html create mode 100644 tag/binary.html create mode 100644 tag/bool.html create mode 100644 tag/boolean.html create mode 100644 tag/bug.html create mode 100644 tag/build.html create mode 100644 tag/busybox.html create mode 100644 tag/byte.html create mode 100644 tag/bytecode.html create mode 100644 tag/c.html create mode 100644 tag/calculus.html create mode 100644 tag/challenge.html create mode 100644 tag/changes.html create mode 100644 tag/class.html create mode 100644 tag/cli.html create mode 100644 tag/code.html create mode 100644 tag/collections.html create mode 100644 tag/compile.html create mode 100644 tag/compiler.html create mode 100644 tag/concurrent.html create mode 100644 tag/concurrentfutures.html create mode 100644 tag/contest.html create mode 100644 tag/control-flow.html create mode 100644 tag/count-words.html create mode 100644 tag/counter.html create mode 100644 tag/course.html create mode 100644 tag/cpu.html create mode 100644 tag/cpython.html create mode 100644 tag/crop.html create mode 100644 tag/crypto.html create mode 100644 tag/cryptography.html create mode 100644 tag/ctf.html create mode 100644 tag/curl.html create mode 100644 tag/cv2.html create mode 100644 tag/cve.html create mode 100644 tag/data-analysis.html create mode 100644 tag/dataclass.html create mode 100644 tag/debugger.html create mode 100644 tag/deep-reading.html create mode 100644 tag/default.html create mode 100644 tag/deque.html create mode 100644 tag/dict.html create mode 100644 tag/dictorder.html create mode 100644 tag/dis.html create mode 100644 tag/disassembly.html create mode 100644 tag/django.html create mode 100644 tag/dos.html create mode 100644 tag/draw.html create mode 100644 tag/dunder.html create mode 100644 tag/editor.html create mode 100644 tag/encoding.html create mode 100644 tag/eol.html create mode 100644 tag/exp.html create mode 100644 tag/extension.html create mode 100644 tag/factorize.html create mode 100644 tag/fast.html create mode 100644 tag/feature.html create mode 100644 tag/features.html create mode 100644 tag/finance.html create mode 100644 tag/fixbug.html create mode 100644 tag/float.html create mode 100644 tag/function.html create mode 100644 tag/gdb.html create mode 100644 tag/generator.html create mode 100644 tag/get.html create mode 100644 tag/grayscale.html create mode 100644 tag/hack.html create mode 100644 tag/hex.html create mode 100644 tag/http.html create mode 100644 tag/ieee.html create mode 100644 tag/ieee754.html create mode 100644 tag/ifelse.html create mode 100644 tag/image.html create mode 100644 tag/import.html create mode 100644 tag/insights.html create mode 100644 tag/int.html create mode 100644 tag/internal.html create mode 100644 tag/interview-question.html create mode 100644 tag/isqrt.html create mode 100644 tag/it.html create mode 100644 tag/itertools.html create mode 100644 tag/jinja2.html create mode 100644 tag/khai-giang.html create mode 100644 tag/khoa-hoc.html create mode 100644 tag/lambda.html create mode 100644 tag/life.html create mode 100644 tag/linux.html create mode 100644 tag/list.html create mode 100644 tag/macos.html create mode 100644 tag/market.html create mode 100644 tag/math.html create mode 100644 tag/methods.html create mode 100644 tag/mime.html create mode 100644 tag/mimetype.html create mode 100644 tag/mttq.html create mode 100644 tag/multiprocesses.html create mode 100644 tag/mypy.html create mode 100644 tag/news.html create mode 100644 tag/no1.html create mode 100644 tag/numpy.html create mode 100644 tag/opencv-python.html create mode 100644 tag/opencv.html create mode 100644 tag/operator.html create mode 100644 tag/or.html create mode 100644 tag/overloading.html create mode 100644 tag/pandas.html create mode 100644 tag/pe.html create mode 100644 tag/poor.html create mode 100644 tag/projecteuler.html create mode 100644 tag/projecteulernet.html create mode 100644 tag/pycurl.html create mode 100644 tag/pyfml.html create mode 100644 tag/pygame.html create mode 100644 tag/pymihn2110.html create mode 100644 tag/pymivn.html create mode 100644 tag/pyspark.html create mode 100644 tag/python.html create mode 100644 tag/python2.html create mode 100644 tag/python3.html create mode 100644 tag/question.html create mode 100644 tag/quiz.html create mode 100644 tag/range.html create mode 100644 tag/rank.html create mode 100644 tag/recursive.html create mode 100644 tag/repr.html create mode 100644 tag/requests.html create mode 100644 tag/rgb.html create mode 100644 tag/rgba.html create mode 100644 tag/rsa.html create mode 100644 tag/salary.html create mode 100644 tag/sao-ke.html create mode 100644 tag/sat.html create mode 100644 tag/security.html create mode 100644 tag/set.html create mode 100644 tag/shared-object.html create mode 100644 tag/slice.html create mode 100644 tag/smt.html create mode 100644 tag/so.html create mode 100644 tag/source.html create mode 100644 tag/spark.html create mode 100644 tag/speed.html create mode 100644 tag/sqrt.html create mode 100644 tag/ssl.html create mode 100644 tag/stackoverflow.html create mode 100644 tag/stdlib.html create mode 100644 tag/str.html create mode 100644 tag/sum.html create mode 100644 tag/survey.html create mode 100644 tag/template.html create mode 100644 tag/theorem-prover.html create mode 100644 tag/threading.html create mode 100644 tag/timeit.html create mode 100644 tag/tiobe.html create mode 100644 tag/tip.html create mode 100644 tag/totp.html create mode 100644 tag/type.html create mode 100644 tag/unicode.html create mode 100644 tag/utf-8.html create mode 100644 tag/van-mau.html create mode 100644 tag/variable.html create mode 100644 tag/version.html create mode 100644 tag/virtual-machine.html create mode 100644 tag/visualization.html create mode 100644 tag/vm.html create mode 100644 tag/vscode.html create mode 100644 tag/web-server.html create mode 100644 tag/z3.html create mode 100644 tag/zip.html create mode 100644 tag/zipapp.html create mode 100644 tag/zlib.html create mode 100644 tags.html create mode 100755 theme/css/data-ttf.css create mode 100755 theme/css/data-woff.css create mode 100755 theme/css/data-woff2.css create mode 100755 theme/css/icons.data.png.css create mode 100755 theme/css/icons.data.svg.css create mode 100755 theme/css/icons.fallback.css create mode 100644 theme/css/main.css create mode 100644 theme/css/main.css.map create mode 100755 theme/css/png/facebook.png create mode 100755 theme/css/png/foursquare.png create mode 100755 theme/css/png/github.png create mode 100755 theme/css/png/goodreads.png create mode 100755 theme/css/png/instagram.png create mode 100755 theme/css/png/medium.png create mode 100755 theme/css/png/strava.png create mode 100755 theme/css/png/telegram.png create mode 100755 theme/css/png/twitter.png create mode 100755 theme/css/png/untappd.png create mode 100644 theme/css/sass/_mixins.sass create mode 100644 theme/css/sass/_pygment.sass create mode 100644 theme/css/sass/_reset.sass create mode 100644 theme/css/sass/main.sass create mode 100755 theme/fonts/lato-black-webfont.ttf create mode 100755 theme/fonts/lato-black-webfont.woff create mode 100755 theme/fonts/lato-black-webfont.woff2 create mode 100755 theme/fonts/lato-light-webfont.ttf create mode 100755 theme/fonts/lato-light-webfont.woff create mode 100755 theme/fonts/lato-light-webfont.woff2 create mode 100755 theme/fonts/lato-regular-webfont.ttf create mode 100755 theme/fonts/lato-regular-webfont.woff create mode 100755 theme/fonts/lato-regular-webfont.woff2 create mode 100644 theme/js/moment.js create mode 100644 tiobeno1.html create mode 100644 topmoney.html create mode 100644 totp.html create mode 100644 towerofhanoi.html create mode 100644 vscode.html create mode 100644 z38q.html create mode 100644 z38qgame.py create mode 100644 z38qgamev2.py create mode 100644 z3factor.html create mode 100644 z3grade3.html create mode 100644 z3ineq.html create mode 100644 z3p1.html create mode 100644 z3pygame.html create mode 100644 z3pygame8q.html create mode 100644 z3speed.html create mode 100644 zip.html create mode 100644 zipapp.html create mode 100644 zlib.html diff --git a/2ty.html b/2ty.html new file mode 100644 index 0000000..84b6fd5 --- /dev/null +++ b/2ty.html @@ -0,0 +1,142 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Làm it bao giờ có 2 tỷ mua nhà? +

+
+

+ by Pymier0 +

+
+
+
+

Năm 2 sau COVID nguyên, ngành IT đã bắt đầu trượt khỏi đỉnh khi hàng ngàn công ty công nghệ lớn nhỏ cắt giảm nhân sự trên toàn cầu. Nhưng trong xã hội, ngành IT vẫn là 1 ngành lương cao. Lương cao thì nhiều tiền, nhiều tiền thì mua nhà, vậy làm bao lâu thì mua được nhà?

+

Bài viết tất nhiên không bàn đến các trường hợp ngoại lệ, hay những người trúng quả coin, startup thành công, viết app riêng vài triệu lượt tải hay đi nộp thuế chục tỷ mỗi năm. Chỉ tính tầng lớp công nhân lập trình lương tháng, tạm thời độc thân. +Cũng không bàn tới đi vay ngân hàng, vì vay thì cũng phải trả chứ.

+

Mua nhà bao tiền

+

stable diffusion house

+

Nhà có nhà thành phố, nhà trên núi, nhà biệt thự, nhà cấp 4, vậy nên nói mua nhà thì chung quá, nên lấy một ví dụ điển hình là mua nhà chung cư ở Hà Nội, khu vực không trung tâm, với mức giá khoảng 2 tỷ. +Lên tìm trên các trang bất động sản có thể thấy

+
+

Nhỉnh 2 tỷ sở hữu ngay căn hộ siêu vip tại Goldsilk Complex dt 68m2, 2 pn - 1wc. Giá: 2,35 tỷ

+
+

2 tỷ 350 là hơn 2 tỷ RẤT NHIỀU (350 triệu mua được 1 cái ô tô), vậy cứ giả vờ là 2 tỷ tròn.

+

IT lương cao là bao nhiêu

+

Theo Báo cáo thị trường IT Việt Nam – Tech Hiring 2022 của Topdev.vn

+
+

Mức lương Lập trình viên làm về các công nghệ thuộc nhóm High Tech bao gồm Tensor Flows, Kubernetes, Python, lần lượt đạt mức $1,732, $1,669, $1,389. +Lập trình viên Senior có mức lương dao động từ $860 đến $1.510. Các vị trí Quản lý (từ 5 năm trở lên) hoặc cấp cao hơn được khảo sát có mức lương từ $1.410 cho đến hơn $2.300.

+
+

Senior tức cỡ tầm 5,7 năm kinh nghiệm++, và giả sử có mức lương mơ ước là $2000 gross.

+

Lương $2000 bao giờ có 2 tỷ mua nhà?

+

$2000 tại thời điểm viết bài có giá trị 47,142,249 VND, đây là lương gross, đổi sang Net (dùng các trang web trên mạng) còn 38,879,871.

+
>>> 2_000_000_000 / 38_900_000
+51.41388174807198
+
+ +

cỡ khoảng 51 tháng, coi như 1 năm có 13 tháng lương (1 tháng thưởng tết), vậy là 4 năm.

+

Vậy sau 4 năm chỉ làm như cái máy kiếm tiền, không ăn uống, không tiêu, không mua gì, giá trị đồng tiền không đổi, thì bạn sẽ có 2 tỷ mua nhà!!!

+

Không ăn sao sống?

+

Chính vì thế, trừ đi tiền nhà thuê, ăn uống, tiêu dùng, mua sắm, nếu mất 1 nửa, thì số năm tăng lên là 8 năm.

+

Chú ý bạn chỉ có lương $2000 sau khi có 5 năm kinh nghiệm, tức khi đủ tiền, là bạn có 5 + 8 == 13 năm đi làm. Đi làm từ năm 23 tuổi, thì khi đủ tiền mua nhà là 23 + 13 = 36.

+

Đó là tính đơn giản, còn nếu lập gia đình, mua bỉm, mua sữa, mua sắm, đóng tiền học… thì còn lâu nữa.

+

Vậy bao giờ thì đủ tiền mua?

+

Chắc 40.

+

Hết.

+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/3c.html b/3c.html new file mode 100644 index 0000000..db49d2e --- /dev/null +++ b/3c.html @@ -0,0 +1,135 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Tách 3 màu rgb từ 1 bức ảnh OpenCV + numpy +

+
+

+ by Pymier0 +

+
+
+
+

img

+

Cách biểu diễn màu trên máy tính phổ biến dùng hệ màu RGB với 3 màu Red Green Blue (đỏ - xanh lục - xanh lam). Khi đọc file ảnh vào bằng opencv, mặc định sẽ có 1 array dạng (dài, rộng, 3), với 3 là 3 “kênh” màu. Để tách riêng 3 kênh màu, ta giữ nguyên 1 kênh, và gán 2 kênh còn lại bằng 0 (sử dụng numpy - vì 1 bức ảnh đọc vào từ opencv là 1 numpy ndarray).

+
# $ python bgr.py ~/Pictures/python-logo.png
+import sys
+import cv2 as cv
+
+cim = cv.imread(sys.argv[1])
+c1 = cim.copy()
+c1[:, :, 1:] = 0
+cv.imwrite("blue.png", c1)
+
+c2 = cim.copy()
+c2[:,:,0] = 0
+c2[:,:,2] = 0
+cv.imwrite("green.png", c2)
+
+c3 = cim.copy()
+c3[:,:,:2] = 0
+cv.imwrite("red.png", c3)
+print("Done")
+
+ +

python-logo +blue +blue +blue

+

Kết quả thu được 3 bức ảnh với màu lần lượt là xanh lam, xanh lục, đỏ (BGR). Theo “cộng đồng mạng StackOverflow”, đó là do “lịch sử” để lại, khi mà OpenCV dùng BGR chứ không theo thứ tự phổ biến RGB.

+

Hết.

+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/CNAME b/CNAME new file mode 100644 index 0000000..9a07924 --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +n.pymi.vn \ No newline at end of file diff --git a/aoc2020-norvig.html b/aoc2020-norvig.html new file mode 100644 index 0000000..88a2e9c --- /dev/null +++ b/aoc2020-norvig.html @@ -0,0 +1,110 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Code Advent of code 2020 của giám đốc nghiên cứu Google - Peter Norvig +

+
+

+ by Pymier0 +

+
+
+
+

Lý do bạn quá bận để tham gia “adventofcode 2020”?

+

Hãy nghĩ lại, khi giám đốc nghiên cứu của Google - Peter Norvig đã xử đẹp 47/50 bài với 1 file jupyter notebook, với code siêu đẹp, siêu ngắn gọn. Và như thường lệ, đây là “văn mẫu” của các pythonista 😍

+

https://github.com/norvig/pytudes/blob/master/ipynb/Advent-2020.ipynb

+

Đăng ký ngay tại PyMI.vn để trở thành lập trình viên #python chuyên nghiệp.

+

Lớp HCM khai giảng tối thứ 3 ngày 12/1/2021.

+

pymi #pymivn #pyfml #adventofcode

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/aoc2022.html b/aoc2022.html new file mode 100644 index 0000000..775d612 --- /dev/null +++ b/aoc2022.html @@ -0,0 +1,123 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Khởi động cuộc thi PyMi AdventOfCode 2022 giải thưởng hấp dẫn +

+
+

+ by Pymier0 +

+
+
+
+

Đến hẹn lại lên, tháng 12 này (từ 1/12 đến 25/12), Pymi.vn sẽ tổ chức thi advent of code, một cuộc thi +code kéo dài 25 ngày. Mỗi ngày trang https://adventofcode.com/ +sẽ đưa ra trc vấn đề vào lúc 12 giờ trưa. Ai tham gia chỉ cần nhập mã 416592-56daa324 vào +https://adventofcode.com/2022/leaderboard/private sau khi tạo tài khoản và đăng nhập.

+

aoc2021 +Photo by Dzung S on Unsplash

+

Khuyến khích mọi người đều tham gia, những bài đầu tiên luôn đủ dễ để làm được và độ khó tăng dần.Cuộc thi không thể hay ho gay cấn nếu không có giải thưởng. Cơ cấu giải:

+
    +
  • giải nhất: 1 thùng bia lon Trúc Bạch + 1 thùng trúc bạch by davinosoft
  • +
  • giải nhì: 3 chai bia Tiger nâu + 1 lốc 6 budweiser by lamdt@vccloud
  • +
  • giải ba: 1 chai dầu ăn Neptune + 1 lốc 6 budweiser by lamdt@vccloud
  • +
+

thanh toán qua chuyển khoản và giá tham khảo.

+

Đối tượng nhận giải: mọi học viên/ cựu học viên của Pymi.vn

+

Mong các nhà hảo tâm ủng hộ đóng góp để giải thưởng thêm to, chia cho 2 3 4 5. liên hệ kênh telegram Pymi

+

PS: ai chưa chơi bao giờ có thể thử đề năm ngoái https://adventofcode.com/2021 +Xem code python của winner 2021 https://github.com/tung491/advent_to_code_2021 +hay blog Pymi https://pp.pymi.vn/article/aoc2021/.

+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/aoc2022e.html b/aoc2022e.html new file mode 100644 index 0000000..8084533 --- /dev/null +++ b/aoc2022e.html @@ -0,0 +1,116 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Tổng kết cuộc thi PyMi AdventOfCode 2022 +

+
+

+ by Pymier0 +

+
+
+
+

Suốt 25 ngày liền trong tháng 12, các code thủ đã vò tai bứt tóc tham gia chiến đấu. +Giải năm nay có tới 19 đấu thủ ghi điểm, vài đấu thủ lập bảng riêng chơi cho “đỡ ngại”???! cuối cùng, các nhà vô địch hái sao đã thuộc về:

+

aoc2022

+
    +
  • Giải nhất: tung491 Python - 2 thùng bia Trúc Bạch
  • +
  • Giải nhì: Amidamaru Rust - 3 chai bia Tiger nâu + 1 lốc 6 budweiser.
  • +
  • Giải ba: Thanh Long Hoàng Python - 1 chai dầu ăn Neptune + 1 lốc 6 budweiser + 1 vé massage khỏe tại Xì Gòn (hiện vật).
  • +
+

Các link trên dẫn tới repo của từng code thủ.

+

Lễ trao giải sẽ được tổ chức tại Hà Nội vào tuần đầu tiên của năm 2023, xin mời tất cả các Pymier tham gia (địa điểm/thời gian báo sau trên kênh telegram Pymi). +Tất cả các game thủ có sao sẽ nhận quà miễn phí 1 cốc bia Hà Nội + 3 chén rượu.

+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/archives.html b/archives.html new file mode 100644 index 0000000..27554f5 --- /dev/null +++ b/archives.html @@ -0,0 +1,3248 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + +
+
+

archives

+
+
+
    +
  1. +
    +
    +

    + + in pymi.vn +

    +
    +
    + +

    Biến nhỏ hơn thành lớn hơn với ast

    +
    +
    +

    AST là gì

    +

    Code Python ngoài dạng dòng text lập trình viên, có thể được biểu diễn ở dạng cây (tree), tên đầy đủ là Abstract Syntax Tree (AST).

    +

    AST giúp việc duyệt qua từng biểu thức (expression), thành phần nhỏ nhất (token) trong mỗi biểu thức trở nên dễ …

    +
    + +
    + +

    Read more...

    +
    +
    +
  2. +
  3. +
    +
    +

    + + in pymi.vn +

    +
    +
    + +

    Một số insights từ file sao kê của Mặt trận Tổ Quốc Việt Nam

    +
    +
    +

    Nhân dịp ngày hội check var phông bạt toàn quốc thứ 6 ngày 13/9/2024, một pymier rảnh rỗi đã ngồi vọc file sao kê từ MTTQ VN. Dưới đây là một số insights từ file sao kê đó (đã convert từ pdf sang csv)

    +

    Tổng số tiền

    +

    Tổng số tiền

    +

    Với …

    +
    + +
    + +

    Read more...

    +
    +
    +
  4. +
  5. +
    +
    +

    + + in pymi.vn +

    +
    +
    + +

    slice[::-1] là slice[len(slice):0:-1] hay slice[0:len(slice):-1]?

    +
    +
    +

    Slice là gì

    +

    Slice là cú pháp tiện dụng của Python để lấy 1 list con của list đã có.

    +
    >>> names = ['c', 'java', 'js', 'php', 'python', 'go', 'rust', 'elixir']
    +
    + +

    Với cú pháp slice[START:STOP:STEP], STARTSTOP là index bắt đầu và kết thúc, +STEP là bước dịch …

    +
    + +
    + +

    Read more...

    +
    +
    +
  6. +
  7. +
    +
    +

    + + in pymi.vn +

    +
    +
    + +

    Lớp Python PyMivn khóa 46 tại Hà Nội khai giảng 7/3/2024

    +
    +
    +

    Đăng ký ngay tại PyMI.vn để trở thành lập trình viên #python chuyên nghiệp.

    +

    img

    +

    Học viên không ở Hà Nội có thể học qua livestream trên Youtube với kênh chat +Slack cùng các học viên Hà Nội.

    +

    Chi tiết tại trang chủ Pymivn. +Thắc mắc/hỏi đáp: slack

    +
    + +
    + +

    Read more...

    +
    +
    +
  8. +
  9. +
    +
    +

    + + in news +

    +
    +
    + +

    pygame - vẽ logo Windows

    +
    +
    +

    Pygame là thư viện làm game với Python, nhưng dùng để vẽ thì cũng không ai phản đối cả. +Lập trình viên JavaScript dễ dàng vẽ hình tròn, vuông với vài dòng code thì với lập trình viên Python, thế giới chỉ có màn hình đen chữ trắng, cần thêm …

    +
    + +
    + +

    Read more...

    +
    +
    +
  10. +
  11. +
    +
    +

    + + in news +

    +
    +
    + +

    mime trên MacOS không như mime trên Ubuntu

    +
    +
    +

    Viết code chạy trên được nhiều hệ điều hành gọi là “cross-platform”, là lý do mà +lập trình viên sẽ dùng các thư viện thay vì “hard-code”.

    +

    Cross-platform file path với os

    +

    Ví dụ đường dẫn thư mục “a” chứa thư mục “b”: trên Linux/MacOS: a/b thì trên …

    +
    + +
    + +

    Read more...

    +
    +
    +
  12. +
  13. +
    +
    +

    + + in news +

    +
    +
    + +

    Lộ mật khẩu cùng dataclass

    +
    +
    +

    dataclasses module là 1 standard library rất mới, xuất hiện ở Python 3.7, với tác dụng:

    +
    +

    This module provides a decorator and functions for automatically adding generated special methods such as __init__() and __repr__() to user-defined classes.

    +
    +

    https://peps.python.org/pep-0557/

    +

    đây là một tính năng được yêu …

    +
    + +
    + +

    Read more...

    +
    +
    +
  14. +
  15. +
    +
    +

    + + in news +

    +
    +
    + +

    Giải bài toán tháp Hà Nội dùng đệ quy

    +
    +
    +

    Tower of Hanoi là một bài toán kinh điển trong ngành khoa học máy tính, được dạy ở mọi trường đại học dạy CNTT ở Việt Nam trong môn “Toán Rời Rạc”.

    +

    Tower of Hanoi không phải của người Việt

    +

    Tower of Hanoi được phát minh bởi nhà toán học …

    +
    + +
    + +

    Read more...

    +
    +
    +
  16. +
  17. +
    +
    +

    + + in pymi.vn +

    +
    +
    + +

    Lớp Python PyMivn khóa 45 tại Hà Nội khai giảng 27/7/2023

    +
    +
    +

    Đăng ký ngay tại PyMI.vn để trở thành lập trình viên #python chuyên nghiệp.

    +

    img

    +

    Photo by NEOM on Unsplash

    +

    Học viên không ở Hà Nội có thể học qua livestream trên Youtube với kênh chat +Slack cùng các học viên Hà Nội.

    +

    Chi tiết tại trang chủ Pymivn. +Thắc …

    +
    + +
    + +

    Read more...

    +
    +
    +
  18. +
  19. +
    +
    +

    + + in news +

    +
    +
    + +

    Django template gọi method không cần ()

    +
    +
    +

    Python có 2 template engine phổ biến nhất: Jinja2 (hay dùng với Flask, SaltStack, Ansible…) và Django template.

    +

    Cả 2 khá giống nhau về cú pháp: viết for/if như Python, dùng function qua các filter có sẵn, đoạn code sau chạy +cho cả 2:

    +
    {% for fw in frameworks %}
    +  {% if …
    +
    + +
    + +

    Read more...

    +
    +
    +
  20. +
  21. +
    +
    +

    + + in news +

    +
    +
    + +

    collections.Counter có phải là dict?

    +
    +
    +

    Lập trình viên Python không bao giờ phải tự đếm số lần xuất hiện của mỗi phần tử, bởi có sẵn Counter:

    +

    Python Counter là gì

    +
    from collections import Counter
    +c = Counter("py py thon thon py".split())
    +print(c)
    +# Counter({'py': 3, 'thon': 2})
    +for k, v in c …
    +
    + +
    + +

    Read more...

    +
    +
    +
  22. +
  23. +
    +
    +

    + + in news +

    +
    +
    + +

    dict.get hay or?

    +
    +
    +

    dict.get(k, default) có bằng dict.get(k) or default?

    +

    or thường được dùng để trả về giá trị mặc định:

    +
    host = os.environ.get("host") or "localhost"
    +
    + +

    Nhưng:

    +

    or có cách hoạt động khá lắt léo:

    +

    các giá trị được biến thành boolean trước khi xử lý, với …

    +
    + +
    + +

    Read more...

    +
    +
    +
  24. +
  25. +
    +
    +

    + + in news +

    +
    +
    + +

    __new__ và __init__

    +
    +
    +

    +new +Photo by Nick Fewings on Unsplash +

    +

    __new____init__ khác gì?

    +

    Như đa phần các câu hỏi phỏng vấn nặng tính lý thuyết, ít ứng dụng thực tế hay trăm năm dùng 1 lần, đây không là ngoại lệ.

    +

    Các câu khác tương tự:

    +
      +
    • decorator là gì (CHƯA bao giờ …
    +
    + +
    + +

    Read more...

    +
    +
    +
  26. +
  27. + +
  28. +
  29. +
    +
    +

    + + in pymi.vn +

    +
    +
    + +

    Lớp Python PyMivn khóa 44 tại Hà Nội khai giảng 11/5/2023

    +
    +
    +

    Đăng ký ngay tại PyMI.vn để trở thành lập trình viên #python chuyên nghiệp.

    +

    img

    +

    Photo by NEOM on Unsplash

    +

    Học viên không ở Hà Nội có thể học qua livestream trên Youtube với kênh chat +Slack cùng các học viên Hà Nội.

    +

    Chi tiết tại trang chủ Pymivn. +Thắc …

    +
    + +
    + +

    Read more...

    +
    +
    +
  30. +
  31. +
    +
    +

    + + in news +

    +
    +
    + +

    zip ngắn, zip dài

    +
    +
    +

    zip (cái khóa kéo) là một function built-in sẵn trong Python, để có được vị trí này - ngang ngửa với len, và print, zip rõ ràng là rất quan trọng.

    +

    zip

    +

    zip - builtin function

    +
    Init signature: zip(self, /, *args, **kwargs)
    +Docstring:
    +zip(*iterables) --> A zip object yielding tuples until an input …
    +
    + +
    + +

    Read more...

    +
    +
    +
  32. +
  33. +
    +
    +

    + + in news +

    +
    +
    + +

    http dùng requests? pycurl nhanh gấp 2!

    +
    +
    +

    curl

    +

    curl - câu lệnh HTTP client phổ biến nhất

    +

    Đó là điều không phải bàn cãi. Đa phần các lập trình viên hay các devops/sysadmin đều ít nhiều biết dùng curl.

    +

    Cài đặt: sudo apt install curl

    +
    $ curl -XGET https://httpbin.org/get
    +{
    +  "args": {},
    +  "headers": {
    +    "Accept": "*/*",
    +    "Host": "httpbin.org",
    +    "User-Agent …
    +
    + +
    + +

    Read more...

    +
    +
    +
  34. +
  35. +
    +
    +

    + + in news +

    +
    +
    + +

    Làm it bao giờ có 2 tỷ mua nhà?

    +
    +
    +

    Năm 2 sau COVID nguyên, ngành IT đã bắt đầu trượt khỏi đỉnh khi hàng ngàn công ty công nghệ lớn nhỏ cắt giảm nhân sự trên toàn cầu. Nhưng trong xã hội, ngành IT vẫn là 1 ngành lương cao. Lương cao thì nhiều tiền, nhiều tiền thì mua …

    +
    + +
    + +

    Read more...

    +
    +
    +
  36. +
  37. +
    +
    +

    + + in pymi.vn +

    +
    +
    + +

    Lớp Python PyMivn khóa 43 tại Hà Nội khai giảng 16/2/2023

    +
    +
    +

    Đăng ký ngay tại PyMI.vn để trở thành lập trình viên #python chuyên nghiệp.

    +

    img

    +

    Photo by 8machine _ on Unsplash

    +

    Học viên không ở Hà Nội có thể học qua livestream trên Youtube với kênh chat +Slack cùng các học viên Hà Nội.

    +

    Chi tiết tại trang chủ Pymivn …

    +
    + +
    + +

    Read more...

    +
    +
    +
  38. +
  39. +
    +
    +

    + + in news +

    +
    +
    + +

    Thêm câu lệnh python vào gdb để in ra binary architecture

    +
    +
    +

    Architecture

    +

    Mỗi file binary được compile cho một kiến trúc(architecture) CPU cụ thể.

    +

    Các architecture phổ biến như:

    +
      +
    • x86-32 (32 bits)
    • +
    • x86-64 (64 bits)
    • +
    • arm-64 (phổ biến trên các máy điện thoại/máy tính bảng/hay Apple M1 M2…)
    • +
    +

    Một file binary đã compile sẵn cho x86-64 thì không …

    +
    + +
    + +

    Read more...

    +
    +
    +
  40. +
  41. +
    +
    +

    + + in news +

    +
    +
    + +

    Python hello world từ debugger gdb

    +
    +
    +

    GDB là gì

    +

    GDB GDB: The GNU Project Debugger là debugger phổ biến bậc nhất thế giới, hỗ +trợ nhiều ngôn ngữ như C, Go, Rust … +Lập trình viên Python không dùng GDB mà dùng pdb với giao diện tương tự gdb, nhưng lập trình viên CPython (core devs) có …

    +
    + +
    + +

    Read more...

    +
    +
    +
  42. +
  43. +
    +
    +

    + + in news +

    +
    +
    + +

    Tổng kết cuộc thi PyMi AdventOfCode 2022

    +
    +
    +

    Suốt 25 ngày liền trong tháng 12, các code thủ đã vò tai bứt tóc tham gia chiến đấu. +Giải năm nay có tới 19 đấu thủ ghi điểm, vài đấu thủ lập bảng riêng chơi cho “đỡ ngại”???! cuối cùng, các nhà vô địch hái sao đã thuộc về …

    +
    + +
    + +

    Read more...

    +
    +
    +
  44. +
  45. +
    +
    +

    + + in news +

    +
    +
    + +

    Khởi động cuộc thi PyMi AdventOfCode 2022 giải thưởng hấp dẫn

    +
    +
    +

    Đến hẹn lại lên, tháng 12 này (từ 1/12 đến 25/12), Pymi.vn sẽ tổ chức thi advent of code, một cuộc thi +code kéo dài 25 ngày. Mỗi ngày trang https://adventofcode.com/ +sẽ đưa ra trc vấn đề vào lúc 12 giờ trưa. Ai tham gia …

    +
    + +
    + +

    Read more...

    +
    +
    +
  46. +
  47. +
    +
    +

    + + in news +

    +
    +
    + +

    Pymi không khuyến khích học viên dùng vscode

    +
    +
    +

    Visual Studio Code, hay vscode, là chương trình editor mã nguồn mở của Microsoft, rất thành công và phổ biến trong giới lập trình viên, hỗ trợ rất nhiều ngôn ngữ, không to nặng như IDE Visual Studio, luôn nằm trong top các editor phổ biến nhất thế giới.

    +

    Gần …

    +
    + +
    + +

    Read more...

    +
    +
    +
  48. +
  49. +
    +
    +

    + + in news +

    +
    +
    + +

    Python 3.10 integer lớn tùy ý, nhưng không còn in được ra màn hình

    +
    +
    +

    Chỉ có sự thay đổi là cố định. Đặc biệt trong ngành IT, sau mỗi 2 năm CPU “nhanh” gấp đôi, công nghệ 2 3 năm lại thay đổi, thì những thứ đúng hôm qua, hôm nay càng chưa chắc còn đúng.

    +

    img

    +

    Vào bài học đầu tiên của lớp PYMI2210 …

    +
    + +
    + +

    Read more...

    +
    +
    +
  50. +
  51. +
    +
    +

    + + in news +

    +
    +
    + +

    Lambda function có default argument

    +
    +
    +

    Từ khóa lambda dùng để tạo function 1 biểu thức trong Python, thường thấy dùng kèm +với sort, ví dụ sắp xếp giảm dần 1 list int:

    +
    >>> sorted([2,3,1], key=lambda x: -x)
    +[3, 2, 1]
    +
    + +

    img

    +

    Có thể gán function được tạo ra từ lambda vào một biến …

    +
    + +
    + +

    Read more...

    +
    +
    +
  52. +
  53. +
    +
    +

    + + in pymi.vn +

    +
    +
    + +

    Lớp Python PyMivn khóa 42 tại Hà Nội khai giảng 18/10/2022

    +
    +
    +

    Đăng ký ngay tại PyMI.vn để trở thành lập trình viên #python chuyên nghiệp.

    +

    img

    +

    Photo by 8machine _ on Unsplash

    +

    Học viên không ở Hà Nội có thể học qua livestream trên Youtube với kênh chat +Slack cùng các học viên Hà Nội.

    +

    Chi tiết tại trang chủ Pymivn …

    +
    + +
    + +

    Read more...

    +
    +
    +
  54. +
  55. +
    +
    +

    + + in news +

    +
    +
    + +

    Dùng C shared object .so trong CPython

    +
    +
    +

    img

    +

    CPython có thể import file C “shared library” (trên Linux là các file “shared object” có đuôi .so bằng việc gõ “import filename”.

    +

    Nếu có cả file .so.py cùng tồn tại, CPython ưu tiên gọi file .so trước.

    +

    Trong stdlib của CPython, có nhiều thư viện dùng C …

    +
    + +
    + +

    Read more...

    +
    +
    +
  56. +
  57. +
    +
    +

    + + in news +

    +
    +
    + +

    zipapp - đóng gói app Python vào 1 file zip chạy được

    +
    +
    +

    img

    +

    Code Python có thể được đóng trong 1 file zip và chạy được (như file .exe trên Windows). Ví dụ đơn giản trên Ubuntu:

    +
    $ mkdir ziptest
    +$ echo 'print("Hello Pymier")' > ziptest/__main__.py
    +$ python3 -m zipapp ziptest
    +$ ls -l ziptest.pyz
    +-rw-rw-r-- 1 hvn hvn 142 Sep 28 21:24 …
    +
    + +
    + +

    Read more...

    +
    +
    +
  58. +
  59. +
    +
    +

    + + in news +

    +
    +
    + +

    totp là gì? quét mã qr để thêm totp là làm gì?

    +
    +
    +

    img

    +

    Ngày nay, khi có đủ chiêu trò để lừa người dùng lấy password, mọi tài khỏan “nghiêm túc” đều cần có xác thực 2FA (2 factors authen) tức cung cấp thêm 1 bí mật khác kèm password. +2FA “lạc hậu” gửi OTP qua tin nhắn, mặc dù việc hack sóng …

    +
    + +
    + +

    Read more...

    +
    +
    +
  60. +
  61. +
    +
    +

    + + in news +

    +
    +
    + +

    mypy là chưa đủ, cần phải nghiêm khắc hơn

    +
    +
    +

    img

    +

    mypy là công cụ không thể thiếu khi code Python ở thập niên thứ 2 thế kỷ 21. +mypy giúp phát hiện hàng loạt lỗi liên quan đến type, mang tới sức mạnh của static typing language tới Python. Nhưng đôi khi, là chưa đủ.

    +

    Thử sức xem bạn có …

    +
    + +
    + +

    Read more...

    +
    +
    +
  62. +
  63. + +
  64. +
  65. +
    +
    +

    + + in news +

    +
    +
    + +

    Python leak var

    +
    +
    +

    img

    +

    Python có một “tính năng” rất bất ngờ nếu bạn đã lập trình các ngôn ngữ khác:

    +
    i = "Bánh Trung Thu"
    +for i in range(5):
    +    print(i)
    +
    +print(i)  # cái này hiện ra gì?
    +
    + +

    tất cả các i trong đoạn code trên đều là 1 i, đầu tiên nó …

    + + +

    Read more...

    +
    +
    +
  66. +
  67. +
    +
    +

    + + in news +

    +
    +
    + +

    [Deep Reading] Chọc sâu vào thư viện dùng phổ biến nhất của Python: lib requests

    +
    +
    +

    img

    +

    Nếu 10 năm trước, khi nói bạn “học sâu”, người ta nghĩ tới việc tìm hiểu thật kỹ, chọc sâu vào qua nhiều tầng lớp để nắm thật chắc kiến thức, thì sau 10 năm marketing, Deep Learning - một nhánh của AI, được dịch sang tiếng Việt với tên “học …

    +
    + +
    + +

    Read more...

    +
    +
    +
  68. +
  69. +
    +
    +

    + + in news +

    +
    +
    + +

    Python3 str là unicode và rắc rối với utf-8

    +
    +
    +

    Python3 chỉ có 1 kiểu string là str. +Python2 có 2 kiểu string: strunicode

    +

    Sự hợp nhất này chính là ưu điểm rất lớn của Python3, lập trình viên không phải đau đầu khi chuyển đổi giữa 2 kiểu string.

    +

    Python3 mặc định sử dụng UTF-8, với …

    +
    + +
    + +

    Read more...

    +
    +
    +
  70. +
  71. +
    +
    +

    + + in pymi.vn +

    +
    +
    + +

    Lớp Python PyMivn khóa 41 tại Hà Nội khai giảng 28/7/2022

    +
    +
    +

    Đăng ký ngay tại PyMI.vn để trở thành lập trình viên #python chuyên nghiệp.

    +

    img

    +

    Học viên không ở Hà Nội có thể học qua livestream trên Youtube với kênh chat +Slack cùng các học viên Hà Nội.

    +

    Chi tiết tại trang chủ Pymivn. +Thắc mắc/hỏi đáp: slack

    +
    + +
    + +

    Read more...

    +
    +
    +
  72. +
  73. + +
  74. +
  75. +
    +
    +

    + + in news +

    +
    +
    + +

    Giới thiệu concurrent.futures trong Python 3

    +
    +
    +

    Concurrency

    +

    Concurrency là khái niệm chương trình thực hiện nhiều công việc cùng một +lúc (dễ thấy ở các chương trình có giao diện đồ họa: vừa hiển thị giao diện, +vừa kết nối đến trang web, hay các chương trình server phục vụ nhiều người dùng +cùng lúc).

    +

    Python …

    + + +

    Read more...

    +
    +
    +
  76. +
  77. +
    +
    +

    + + in pymi.vn +

    +
    +
    + +

    Lớp Python PyMivn khóa 40 tại Hà Nội khai giảng 19/5/2022

    +
    +
    +

    Đăng ký ngay tại PyMI.vn để trở thành lập trình viên #python chuyên nghiệp.

    +

    img

    +

    Học viên không ở Hà Nội có thể học qua livestream trên Youtube với kênh chat +Slack cùng các học viên Hà Nội.

    +

    Chi tiết tại trang chủ Pymivn. +Thắc mắc/hỏi đáp: slack

    +
    + +
    + +

    Read more...

    +
    +
    +
  78. +
  79. +
    +
    +

    + + in news +

    +
    +
    + +

    rgba == rgb + alpha

    +
    +
    +

    img

    +

    Bài trước giới thiệu 3 kênh màu RGB thường dùng để biểu diễn +ảnh màu trên máy tính, với OpenCV dùng hơi ngược 1 chút: BGR.

    +

    Ngoài 3 kênh này, ảnh còn có thể có thêm 1 kênh thứ 4 nữa gọi là alpha. Và RGB +giờ sẽ gọi là …

    +
    + +
    + +

    Read more...

    +
    +
    +
  80. +
  81. +
    +
    +

    + + in news +

    +
    +
    + +

    Tách 3 màu rgb từ 1 bức ảnh OpenCV + numpy

    +
    +
    +

    img

    +

    Cách biểu diễn màu trên máy tính phổ biến dùng hệ màu RGB với 3 màu Red Green Blue (đỏ - xanh lục - xanh lam). Khi đọc file ảnh vào bằng opencv, mặc định sẽ có 1 array dạng (dài, rộng, 3), với 3 là 3 “kênh” màu. Để tách riêng …

    +
    + +
    + +

    Read more...

    +
    +
    +
  82. +
  83. +
    +
    +

    + + in news +

    +
    +
    + +

    OpenCV đọc ảnh đen trắng (grayscale)

    +
    +
    +

    Ảnh màu trên máy tính thường sử dụng hệ màu red-green-blue (đỏ - xanh lục - xanh lam) viết tắt là RGB. Bộ màu này có khả năng biểu diễn 256 * 256 * 256 = 16777216 (16 triệu màu), mặc định OpenCV đọc ảnh vào ở dạng ảnh màu. Xem shape của ảnh sau …

    + + +

    Read more...

    +
    +
    +
  84. +
  85. +
    +
    +

    + + in news +

    +
    +
    + +

    Xin chào opencv-python

    +
    +
    +

    img

    +

    OpenCV (https://opencv.org/)

    +
    +

    OpenCV (Open Source Computer Vision Library: http://opencv.org) is an open-source library that includes several hundreds of computer vision algorithms.

    +
    +

    là một thư viện xử lý hình ảnh (ảnh, video, …) viết bằng C++ nhưng có thể dùng từ nhiều ngôn ngữ khác nhau, trong Python …

    +
    + +
    + +

    Read more...

    +
    +
    +
  86. +
  87. +
    +
    +

    + + in news +

    +
    +
    + +

    Số float lớn nhất

    +
    +
    +

    img

    +

    Bài trước sau khi thử lấy 2**2048/2, xảy ra exception

    +
    +

    OverflowError: int too large to convert to float

    +
    +

    float không lớn tùy ý như int.

    +

    Số float lớn nhất? là inf

    +
    >>> float('inf')
    +inf
    +
    + +

    Số float lớn nhất sau inf?

    +
    >>> import sys
    +>>> sys.float_info.max
    +1.7976931348623157e+308 …
    +
    + +
    + +

    Read more...

    +
    +
    +
  88. +
  89. +
    +
    +

    + + in news +

    +
    +
    + +

    Tính căn số lớn

    +
    +
    +

    img

    +

    Trong Python, kiểu int có độ lớn tùy ý, như 2 mũ 2048:

    +
    >>> b = 2**2048
    +>>> b
    +32317006071311007300714876688669951960444102669715484032130345427524655138867890893197201411522913463688717960921898019494119559150490921095088152386448283120630877367300996091750197750389652106796057638384067568276792218642619756161838094338476170470581645852036305042887575891541065808607552399123930385521914333389668342420684974786564569494856176035326322058077805659331026192708460314150258592864177116725943603718461857357598351152301645904403697613233287231227125684710820209725157101726931323469678542580656697935045997268352998638215525166389437335543602135433229604645318478604952148193555853611059596230656
    +
    + +

    cộng trừ vẫn thoải mái, nhưng… tính căn 2 (hoặc chia) thì sao?

    +
    >>> from math import sqrt
    +>>> sqrt(b)
    +Traceback (most recent call last):
    +  File "<stdin>", line 1, in <module>
    +OverflowError: int …
    +
    + +
    + +

    Read more...

    +
    +
    +
  90. +
  91. +
    +
    +

    + + in news +

    +
    +
    + +

    Các method của boolean True False

    +
    +
    +

    img

    +

    True và False là hai giá trị kiểu bool trong Python, có thể nói thuộc một trong những thứ đơn giản nhất, vì chỉ có hai giá trị.

    +

    Sau khi học hành tử tế sẽ biết các operator and or not và chúng có tính short-circuit, và… hết.

    +

    Hỏi: giá …

    +
    + +
    + +

    Read more...

    +
    +
    +
  92. +
  93. +
    +
    +

    + + in news +

    +
    +
    + +

    Base16 là gì? base là gì?

    +
    +
    +

    img

    +

    Bài trước giới thiệu Base64 vì nó sử dụng 64 ký tự A-Za-z0-9+/ để biểu diễn binary data tùy ý. +Ngoài Base64, trong tài liệu định nghĩa Base64 còn nhắc tới: Base16, Base32, and Base64.

    +

    Base16

    +

    Base16 sử dụng 16 ký tự để biểu diễn binary: 0-9a-f (hoặc viết hoa …

    +
    + +
    + +

    Read more...

    +
    +
    +
  94. +
  95. +
    +
    +

    + + in news +

    +
    +
    + +

    Thư viện base64 trong Python được viết thế nào

    +
    +
    +

    img

    +

    Bài trước đã tự viết code Python để encoding từ bytes sang base64, code đơn giản, mang tính chất minh họa. Vậy stdlib base64 Python viết thế nào?

    +

    https://github.com/python/cpython/blob/3.10/Lib/base64.py#L51-L62

    +
    # Base64 encoding/decoding uses binascii
    +
    +def b64encode(s, altchars=None …
    +
    + +
    + +

    Read more...

    +
    +
    +
  96. +
  97. +
    +
    +

    + + in news +

    +
    +
    + +

    Base64 là gì? tại sao lại là 64?

    +
    +
    +

    img

    +

    Khi lập trình, hay làm sysadmin, một khái niệm đâu đó với tên encode Base64 xuất hiện và không quá khó để dùng:

    +

    Lệnh trên Ubuntu Linux:

    +
    echo -n PyMi.vn | base64
    +UHlNaS52bg==
    +
    + +

    Hay Python3:

    +
    >>> import base64
    +>>> base64.b64encode("PyMi.vn")
    +Traceback (most recent call last):
    +  File "<stdin>", line 1 …
    +
    + +
    + +

    Read more...

    +
    +
    +
  98. +
  99. +
    +
    +

    + + in news +

    +
    +
    + +

    Duyệt qua mọi đáp án bài toán 8 hậu trên Pygame với Z3

    +
    +
    +

    img

    +

    Bài trước đã hiển thị 1 đáp án bài toán 8 hậu giải bằng Z3 lên Pygame. Bài này sẽ hiển thị mọi đáp án, mỗi lần người dùng nhấn phím SPACE sẽ chuyển sang đáp án tiếp theo.

    +

    Trong Pygame để nhận việc người dùng nhấn phím, dùng pygame …

    +
    + +
    + +

    Read more...

    +
    +
    +
  100. +
  101. +
    +
    +

    + + in news +

    +
    +
    + +

    Giải bài toán 8 hậu trên Pygame với Z3

    +
    +
    +

    img

    +

    Pygame vốn để làm game, nhưng cũng là một công cụ tiện lợi để hiển thị các vấn đề. +Tải pygame pip install pygame rồi viết code:

    +

    Hiện 1 cửa sổ vuông tương ứng với bàn cờ, kích thước 800x800 pixel:

    +
    import time
    +import pygame
    +pygame.init()
    +
    +display = pygame.display …
    +
    + +
    + +

    Read more...

    +
    +
    +
  102. +
  103. +
    +
    +

    + + in news +

    +
    +
    + +

    Giải bài toán 8 quân hậu bằng Z3

    +
    +
    +

    img

    +

    Bài toán 8 quân hậu: bàn cờ vua kích thước 8x8, có 8 quân hậu, tìm vị trí sao cho 8 con không ăn được nhau (không cùng hàng ngang, cột, chéo). Bài toán này được giải bằng thuật toán backtracking.

    +

    Backtracking giải bài này là thử đặt 1 quân …

    + + +

    Read more...

    +
    +
    +
  104. +
  105. +
    +
    +

    + + in news +

    +
    +
    + +

    Z3 nhanh chậm ra sao?

    +
    +
    +

    img

    +

    Z3 giải toán vi diệu là vậy, nhưng mỗi lần tìm ra 1 nghiệm thôi, vậy tìm 1000 nghiệm của bất phương trình 0 < x < 1001 thì mất bao lâu?

    +

    Cách làm tương tự phần trước, ta tìm ra 1 nghiệm rồi thêm nghiệm đó vào điều kiện loại trừ …

    +
    + +
    + +

    Read more...

    +
    +
    +
  106. +
  107. +
    +
    +

    + + in news +

    +
    +
    + +

    Tìm tất cả nghiệm của bất phương trình bằng Z3

    +
    +
    +

    img

    +

    Các bài trước dùng Z3 để trả về 1 nghiệm của bài toán (do Z3 quyết định), lần này +ta sẽ viết thêm code để khiến Z3 tìm thêm các nghiệm cho tới khi đủ nghiệm.

    +

    Cho bất phương trình: x + y < 4, với x, y nguyên dương. +Dễ mò …

    +
    + +
    + +

    Read more...

    +
    +
    +
  108. +
  109. +
    +
    +

    + + in news +

    +
    +
    + +

    Giải “Bài toán lớp 3 có số lượng đáp án khổng lồ” bằng Z3

    +
    +
    +

    Bài toán lớp 3:

    +

    img

    +
    a + 13 * b / c + d + 12 * e – f – 11 + g * h / i – 10 = 66
    +
    + +

    lop3

    +

    Tìm các số nguyên dương < 10, a đến i thoả mãn bài toán (tìm một nghiệm của bài toán)

    +

    Giải bằng Z3:

    +
    from z3 import solve, Ints
    +a,b,c,d,e …
    +
    + +
    + +

    Read more...

    +
    +
    +
  110. +
  111. +
    +
    +

    + + in news +

    +
    +
    + +

    k in dict.keys() chậm hơn k in dict bao nhiêu?

    +
    +
    +

    img

    +

    Python2, khi gọi dict.keys() sẽ trả về 1 list các key của dict. Một cải tiến +lớn của Python3 là không trả về list mà trả về những thứ tương tự +generator để tiết kiệm bộ nhớ.

    +
    In [32]: type({}.keys())
    +Out[32]: dict_keys
    +
    +In [33]: type({}.items())
    +Out …
    +
    + +
    + +

    Read more...

    +
    +
    +
  112. +
  113. +
    +
    +

    + + in news +

    +
    +
    + +

    Giải toán lớp 4: 50 cặp chân gà, chó bằng Z3

    +
    +
    +

    img

    +

    Z3 là một “theorem prover”, thuộc dạng Satisfiability Modulo Theories (SMT) solver, một sản phẩm của Microsoft Research. Nó có thể làm rất nhiều thứ, một cách “magic”, không dễ để hiểu, nhưng không khó để dùng.

    +

    Loạt bài sẽ dùng Z3 để giải các bài toán đố chỉ bằng …

    +
    + +
    + +

    Read more...

    +
    +
    +
  114. +
  115. + +
  116. +
  117. +
    +
    +

    + + in news +

    +
    +
    + +

    Gửi 100 triệu vào ngân hàng hôm nay, bao giờ có 1 tỷ?

    +
    +
    +

    img

    +

    Bài trước đã giới thiệu khái niệm lãi gộp (compound interest) +với lãi suất 1%. +Lãi suất ngân hàng Vietcombank tại thời điểm viết bài là 5.5%/năm, sau 13 năm +sẽ gấp đôi, sau 44 năm +ta sẽ có 1 tỷ (gấp 10), sau 100 năm sẽ có …

    +
    + +
    + +

    Read more...

    +
    +
    +
  118. +
  119. +
    +
    +

    + + in news +

    +
    +
    + +

    Compound Interest

    +
    +
    +
    +

    1% lãi mỗi năm, thì bao giờ tiền gửi sẽ gấp đôi?

    +
    +

    Câu trả lời sai dễ dàng là 100 năm, vì mỗi năm là 1%, 100 * 1 == 100.

    +

    img

    +

    Điều kỳ diệu ở đây là phần lãi 1% này được gộp vào để tính lãi sau năm đầu tiên. +Năm …

    +
    + +
    + +

    Read more...

    +
    +
    +
  120. +
  121. +
    +
    +

    + + in pymi.vn +

    +
    +
    + +

    Lớp học Python PyMi Hà Nội + tp Hồ Chí Minh khai giảng 21/10/2021

    +
    +
    +

    Học viên không ở HN sẽ học qua livestream youtube, chat qua kênh Slack chung +với lớp Hà Nội offline.

    +

    img

    +

    Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

    + + +

    Read more...

    +
    +
    +
  122. +
  123. + +
  124. +
  125. +
    +
    +

    + + in news +

    +
    +
    + +

    Build Python từ source với zlib và ssl trên Ubuntu 20.04

    +
    +
    +

    Tiếp bài trước, sau khi compile CPython thành công, ta import thử vài thư viện

    +
    >>> import gzip
    +Traceback (most recent call last):
    +  File "<stdin>", line 1, in <module>
    +  File "/root/Python-3.9.7/Lib/gzip.py", line 9, in <module>
    +    import zlib
    +ModuleNotFoundError: No module named 'zlib'
    +>>> import ssl
    +Traceback …
    +
    + +
    + +

    Read more...

    +
    +
    +
  126. +
  127. +
    +
    +

    + + in news +

    +
    +
    + +

    Build Python từ source trên Ubuntu 20.04

    +
    +
    +

    Build phần mềm từ source dù là chuyện phổ biến trong giới mã nguồn mở từ xưa nhưng dần dần trở thành bí kíp thất +truyền với việc các package manager (như apt, yum, …) đều cài sẵn binary (sản phẩm của việc build).

    +

    Build từ source không tiện lợi cho …

    +
    + +
    + +

    Read more...

    +
    +
    +
  128. +
  129. +
    +
    +

    + + in news +

    +
    +
    + +

    PySpark đi phỏng vấn đếm từ count words

    +
    +
    +

    count words” là 1 bài phỏng vấn kinh điển trong ngành IT, nó không quá khó/thuật toán/thách đố, mà lại rất thực tế, yêu cầu đủ các kiến thức cần có để viết code:

    +
      +
    • dùng dictionary
    • +
    • viết vòng lặp for
    • +
    • có thể cần viết if
    • +
    • xử lý string …
    + + +

    Read more...

    +
    +
    +
  130. +
  131. +
    +
    +

    + + in news +

    +
    +
    + +

    PySpark bigdata giải bài toán ProjectEuler 1

    +
    +
    +

    pandas là công cụ tuyệt vời để xử lý, khám phá dữ liệu dạng bảng như Excel, nó là phần không thể thiếu với các ngành “data analysis”, “data science”.

    +

    pandas có 1 nhược điểm/yêu cầu: là dữ liệu phải nhét vừa vào RAM. Tức nếu có bộ dữ …

    + + +

    Read more...

    +
    +
    +
  132. +
  133. +
    +
    +

    + + in features +

    +
    +
    + +

    chạy web server bằng 1 câu lệnh Python

    +
    +
    +

    Python có sẵn trong stdlib thư viện http để bật ngay 1 HTTP server/webserver, +khi chạy THẬT, bạn có thể dùng NGINX, nhưng đôi khi cần “test nhanh”, cài và +config NGINX là một chuyện không hề nhanh.

    +

    img

    +

    Gõ:

    +
    $ python3 -m http.server
    +Serving HTTP on 0.0.0 …
    +
    + +
    + +

    Read more...

    +
    +
    +
  134. +
  135. +
    +
    +

    + + in features +

    +
    +
    + +

    Test nhanh tốc độ cpu dùng Python

    +
    +
    +

    https://cpu.pymi.vn/ cho ta một cách để so sánh các CPU với nhau, đồng thời nắm được tốc độ của Python. Nhưng khi cần test nhanh tốc độ CPU trên máy mà ko muốn tải/code nhiều thì làm sao?

    +

    Việc làm này rất hữu dụng khi test …

    +
    + +
    + +

    Read more...

    +
    +
    +
  136. +
  137. +
    +
    +

    + + in features +

    +
    +
    + +

    Dịch ngược mã máy Python: import

    +
    +
    +

    Tiếp loạt bài về CPython compiler

    +

    img

    +
     rand.py 
    +     1  import random
    +     2  do = random.choice(['an', 'ngu'])
    +     3  print(do)
    +     4
    +     5  from datetime import timedelta
    +     6  print("Mot ngay co: {}s".format(timedelta(days=1).total_seconds()))
    +     7
    +     8  from math import *
    +     9  print(factorial(5))
    +
    + +

    3 kiểu import trong …

    + + +

    Read more...

    +
    +
    +
  138. +
  139. + +
  140. +
  141. +
    +
    +

    + + in news +

    +
    +
    + +

    Python làm chủ mọi cuộc chơi ieee 2021

    +
    +
    +

    Bảng xếp hạng của IEEE năm 2021 về mức độ phổ biến, Python lại một lần nữa đứng số 1, +ở mọi tiêu chí sắp xếp.

    +

    Từ lập trình nhúng, tới lập trình web, tới hệ thống AI cỡ siêu to khổng lồ, +Python xuất hiện ở mọi nơi.

    +

    img

    +

    Link …

    +
    + +
    + +

    Read more...

    +
    +
    +
  142. +
  143. +
    +
    +

    + + in features +

    +
    +
    + +

    Dịch ngược mã máy Python: for/while

    +
    +
    +

    Tiếp loạt bài về CPython compiler

    +

    img

    +

    Xem ví dụ tính tổng 1 tới 100 sau:

    +

    for

    +
    # for.py
    +t = 0
    +for i in range(1, 101):
    +    t = t + i
    +
    +print(t)
    +
    + +

    Chạy dis

    +
    $ python3 -m dis for.py
    +  1           0 LOAD_CONST               0 (0)
    +              2 STORE_NAME               0 (t)
    +
    +  2           4 LOAD_NAME …
    + + +

    Read more...

    +
    +
    +
  144. +
  145. +
    +
    +

    + +

    +
    +
    + +

    Dịch ngược mã máy Python: if/else

    +
    +
    +

    img

    +

    Tiếp loạt bài về CPython compiler

    +
    # ifelse.py
    +x = 20
    +if x > 18:
    +    print("18+")
    +else:
    +    print("vaccine is coming")
    +
    + +
     $ python3 -m dis i
    +  1           0 LOAD_CONST               0 (20)
    +              2 STORE_NAME               0 (x)
    +
    +  2           4 LOAD_NAME                0 (x)
    +              6 LOAD_CONST               1 (18)
    +              8 COMPARE_OP               4 (>)
    +             10 POP_JUMP_IF_FALSE       22
    +
    +  3          12 …
    + + +

    Read more...

    +
    +
    +
  146. +
  147. +
    +
    +

    + + in features +

    +
    +
    + +

    Hello dis

    +
    +
    +

    Tiếp loạt bài về Python compiler.

    +

    img

    +

    Trong CPython compiler, ta đã biết CPython có thực +hiện việc compile code, sinh ra các bytecode, rồi bytecode sẽ được CPython VM +chạy. Khi import module, CPython sinh ra các file .pyc sau khi compile. Nội dung +các file này ở dạng binary …

    + + +

    Read more...

    +
    +
    +
  148. +
  149. +
    +
    +

    + + in features +

    +
    +
    + +

    CPython compiler

    +
    +
    +

    img

    +

    Python thường được biết tới như một ngôn ngữ lập trình scripting/interpreted. +Lập trình viên sau khi viết code xong, chỉ cần gõ python tên_file.py để chạy +code dẫn tới một sự hiểu nhầm phổ biến rằng “python không compile code”.

    +

    Để tránh các tranh cãi không cần …

    + + +

    Read more...

    +
    +
    +
  150. +
  151. +
    +
    +

    + + in features +

    +
    +
    + +

    Tự làm phép cộng

    +
    +
    +

    img

    +

    Các phép toán trong Python thực chất là các “syntactic sugar”, giúp cho viết +gọn hơn thay vì dùng các method thực sự bên dưới. Phép cộng, nói 1 cách đơn +giản, sẽ gọi method __add__. Việc này làm thay đổi ý nghĩa thông thường của +dấu + để cộng các …

    + + +

    Read more...

    +
    +
    +
  152. +
  153. +
    +
    +

    + + in news +

    +
    +
    + +

    StackOverflow khảo sát thị trường 2021: Python most wanted

    +
    +
    +

    img

    +

    StackOverflow.com - trang web +không thể thiếu của mọi lập trình viên (để hỏi đáp), đã công bố kết quả khảo sát +thị trường năm 2021 thực hiện từ tháng 5 đến tháng 6 2021. Kết quả đầy điều thú vị +với lập trình viên Python:

    +
      +
    • Python vượt SQL, chiếm …
    + + +

    Read more...

    +
    +
    +
  154. +
  155. +
    +
    +

    + + in news +

    +
    +
    + +

    ProjectEuler 16

    +
    +
    +

    PE16:

    +
    +

    Tính tổng các chữ số của 2 mũ 1000.

    +
    +

    img

    +

    Một bài toán quen thuộc đối với các học viên pymivn, sau 4 buổi học dễ dàng giải bằng 1 dòng code:

    +
    >>> sum(int(i) for i in str(2**1000))
    +
    + +

    đây là ví dụ tuyệt vời để minh họa …

    + + +

    Read more...

    +
    +
    +
  156. +
  157. +
    +
    +

    + + in news +

    +
    +
    + +

    Kỳ diệu range

    +
    +
    +

    Bất kỳ ai học Python cũng đều biết đến range, function giúp tạo 1 list các +số nguyên:

    +
    range(1, 100)
    +
    + +

    img

    +

    range có trong Python từ rất lâu rồi, thời Python2, range có kèm 1 người +anh em song sinh tên gọi xrange. xrange này chính là range ở Python3 …

    +
    + +
    + +

    Read more...

    +
    +
    +
  158. +
  159. +
    +
    +

    + + in pymi.vn +

    +
    +
    + +

    Lớp Python PyMivn khóa 37 tại Hà Nội khai giảng 1/7/2021

    +
    +
    +

    Đăng ký ngay tại PyMI.vn để trở thành lập trình viên #python chuyên nghiệp.

    +

    img

    +

    Học viên không ở Hà Nội có thể học qua livestream trên Youtube với kênh chat +Slack cùng các học viên Hà Nội.

    +

    Lịch học: link

    +

    Chi tiết tại trang chủ Pymivn. +Thắc mắc/hỏi …

    +
    + +
    + +

    Read more...

    +
    +
    +
  160. +
  161. +
    +
    +

    + + in news +

    +
    +
    + +

    Tôi muốn … em

    +
    +
    +

    ... là một keyword hợp lệ trong Python, đã có từ rất lâu, và rất ít được biết tới.

    +

    img

    +

    ... trong tiếng Anh là Ellipsis, trong Python có thể viết 1 trong 2 giá trị này, chúng có kiểu ellipsis chữ e viết thường.

    +
    >>> x = ...
    +>>> type(x)
    +<class 'ellipsis'>
    +>>> Ellipsis is ...
    +True …
    +
    + +
    + +

    Read more...

    +
    +
    +
  162. +
  163. +
    +
    +

    + + in news +

    +
    +
    + +

    Là và bằng

    +
    +
    +

    Nếu học Python tại Pymi.vn, chắc chắn bạn biết 1 bí mật ít khi dùng tới: 1 == True và 0 == False:

    +
    >>> True == 1 and False == 0
    +True
    +
    + +

    Thậm chí có thể đem tính toán:

    +
    >>> True + True - False
    +2
    +
    + +

    bí mật này dẫn tới một số vấn đề “kỳ lạ …

    +
    + +
    + +

    Read more...

    +
    +
    +
  164. +
  165. + +
  166. +
  167. +
    +
    +

    + + in news +

    +
    +
    + +

    Enum trong Python

    +
    +
    +

    Khái niệm Enum có trong hầu hết các ngôn ngữ static typing nhưng mãi đến 3.4 +mới xuất hiện trong Python standard lib enum. +Tức Python developers đã nghĩ ra cách để không phải dùng +enum cũng chẳng sao… nhưng có thì tốt.

    +

    img

    +

    Enum để làm gì?

    +

    Enum là …

    +
    + +
    + +

    Read more...

    +
    +
    +
  168. +
  169. +
    +
    +

    + + in news +

    +
    +
    + +

    Lập trình viên Python có mức lương cao nhất tại Việt Nam 2021

    +
    +
    +

    Theo Báo Cáo Thị Trường IT Việt Nam 2021 – Developers Recruitment State, +lập trình viên dùng “công nghệ” Python có mức lương cao nhất, ngôn ngữ theo sát +sau là Golang.

    +

    topmoney

    +

    MỨC LƯƠNG CỦA LẬP TRÌNH VIÊN THEO CÔNG NGHỆ +(một cách tương đối dành cho đối tượng có tới …

    +
    + +
    + +

    Read more...

    +
    +
    +
  170. +
  171. +
    +
    +

    + + in news +

    +
    +
    + +

    Đáp án vì sao không tool nào thấy bug

    +
    +
    +

    Trong bài trước, một ví dụ trông đơn giản +và dễ phát hiện lỗi bằng mắt việc x chưa được định nghĩa nhưng không tool nào +của Python phát hiện ra.

    +
    def n_pymi_vn() -> int:
    +    s = x + 1
    +    return s
    +
    +
    +r = n_pymi_vn()
    +x = 10
    +print(r)
    +
    + +

    img

    +

    Lý do là bởi khái …

    +
    + +
    + +

    Read more...

    +
    +
    +
  172. +
  173. +
    +
    +

    + + in news +

    +
    +
    + +

    Vì sao không chương trình nào phát hiện ra lỗi này?

    +
    +
    +
    def n_pymi_vn() -> int:
    +    s = x + 1
    +    return s
    +
    +
    +r = n_pymi_vn()
    +x = 10
    +print(r)
    +
    + +

    Đoạn code này có bug gì? tại sao các tool không phát hiện ra? +và nên sửa thế nào để các tool có thể phát hiện ra?

    +

    Xóa 3 dòng cuối đi, flake8 hay mypy sẽ …

    +
    + +
    + +

    Read more...

    +
    +
    +
  174. +
  175. +
    +
    +

    + + in features +

    +
    +
    + +

    Thư viện pathlib trong Python3.4

    +
    +
    +

    img

    +

    Làm thế nào để viết code mở 1 file ở ngay cùng thư mục file code hiện tại?

    +
    open("./data.csv")
    +
    + +

    cách này sẽ mở file cùng thư mục làm việc hiện tại (current work directory - +cwd), mặc định là thư mục người dùng gõ lệnh để chạy Python script …

    +
    + +
    + +

    Read more...

    +
    +
    +
  176. +
  177. +
    +
    +

    + + in features +

    +
    +
    + +

    CPython list thực chất là 1 array

    +
    +
    +

    img

    +

    Trong CPython (bản/implementation Python phổ biến nhất - tải tại python.org), +kiểu dữ liệu list thực chất là 1 array tương tự như array trong các ngôn ngữ +lập trình khác (C/Java…). +Nó không phải kiểu linked-list mà trong ngành khoa học máy tính thường hay +gọi là …

    +
    + +
    + +

    Read more...

    +
    +
    +
  178. +
  179. +
    +
    +

    + + in features +

    +
    +
    + +

    Từ Python3.7 trở đi, các key trong dict có thứ tự

    +
    +
    +

    img

    +

    Các key sẽ theo thứ tự chúng được thêm vào dict.

    +
    In [1]: for i in {1:None, 3: None, 20: None, 2: None}:
    +   ...:     print(i)
    +   ...:
    +1
    +3
    +20
    +2
    +
    + +

    khác với trước kia, các key không có thứ tự (khác với ngẫu nhiên - ngẫu nhiên +là mỗi lần chạy …

    +
    + +
    + +

    Read more...

    +
    +
    +
  180. +
  181. +
    +
    +

    + + in news +

    +
    +
    + +

    Python 3.6 sẽ hết thời vào tháng 12 2021

    +
    +
    +

    img

    +

    Có thể bạn đã biết, bản #python 3 ngon lành đầu tiên được dùng siêu rộng rãi là +3.6 sẽ hết hạn (EOL) vào cuối năm nay. Đã đến lúc học dần các tính năng mới của +3.8 và upgrade dần đi thôi 😜

    +

    https://devguide.python.org/#status-of-python-branches …

    +
    + +
    + +

    Read more...

    +
    +
    +
  182. +
  183. +
    +
    +

    + + in news +

    +
    +
    + +

    Code Advent of code 2020 của giám đốc nghiên cứu Google - Peter Norvig

    +
    +
    +

    Lý do bạn quá bận để tham gia “adventofcode 2020”?

    +

    Hãy nghĩ lại, khi giám đốc nghiên cứu của Google - Peter Norvig đã xử đẹp 47/50 bài với 1 file jupyter notebook, với code siêu đẹp, siêu ngắn gọn. Và như thường lệ, đây là “văn mẫu” của các …

    + + +

    Read more...

    +
    +
    +
  184. +
  185. +
    +
    +

    + + in pymi.vn +

    +
    +
    + +

    Lớp Python PyMivn khóa 35 tại Sài Gòn - tp Hồ Chí Minh khai giảng 12/1/2021

    +
    +
    +

    Đăng ký ngay tại PyMI.vn để trở thành lập trình viên #python chuyên nghiệp.

    +

    Lớp HCM khai giảng tối thứ 3 ngày 12/1/2021.

    +

    Lịch học: link

    +

    Chi tiết tại trang chủ Pymivn. +Thắc mắc/hỏi đáp: slack

    +
    + +
    + +

    Read more...

    +
    +
    +
  186. +
+
+
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/ast_intro.html b/ast_intro.html new file mode 100644 index 0000000..af015ca --- /dev/null +++ b/ast_intro.html @@ -0,0 +1,261 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Biến nhỏ hơn thành lớn hơn với ast +

+
+

+ by Pymier0 +

+
+
+
+

AST là gì

+

Code Python ngoài dạng dòng text lập trình viên, có thể được biểu diễn ở dạng cây (tree), tên đầy đủ là Abstract Syntax Tree (AST).

+

AST giúp việc duyệt qua từng biểu thức (expression), thành phần nhỏ nhất (token) trong mỗi biểu thức trở nên dễ dàng hơn nhiều so với dạng text, vì vậy các tool check code ưa chuộng việc dùng AST thay text.

+

Biến code text thành tree

+

Sử dụng ast.parse để biến code thành tree. Dùng ast.dump để hiển thị cây ở dạng text:

+
>>> import ast
+>>> print(ast.dump(ast.parse('1+1<2'), indent=4))
+Module(
+    body=[
+        Expr(
+            value=Compare(
+                left=BinOp(
+                    left=Constant(value=1),
+                    op=Add(),
+                    right=Constant(value=1)),
+                ops=[
+                    Lt()],
+                comparators=[
+                    Constant(value=2)]))],
+    type_ignores=[])
+
+ +

AST không bị ảnh hưởng khi thêm space vào giữa các token, hay thậm chí xuống dòng, comment:

+
(1+1
+
+ <
+#<
+ 2)
+
+ +
>>> import ast
+>>> print(ast.dump(ast.parse("""(1+1
+...
+... <
+... #<
+... 2)"""), indent=4))
+Module(
+    body=[
+        Expr(
+            value=Compare(
+                left=BinOp(
+                    left=Constant(value=1),
+                    op=Add(),
+                    right=Constant(value=1)),
+                ops=[
+                    Lt()],
+                comparators=[
+                    Constant(value=2)]))],
+    type_ignores=[])
+
+ +

kết quả vẫn giống với 1+1<2.

+

Biến nhỏ hơn thành lớn hơn

+

Đề bài: biến phép toán so sánh nhỏ hơn thành lớn hơn.

+

Nếu sử dụng biến đổi text, ta có thể viết:

+
>>> s = "1+1<2"
+>>> s.replace("<", ">")
+'1+1>2'
+
+ +

đơn giản! và sai nếu:

+
    +
  • s là 1+1<<2 (binary shift)
  • +
  • s là 1 dòng comment
  • +
  • hay s là 1 string ‘1+1<2’.
  • +
+

Mọi giải pháp sử dụng biến đổi text (string method/regex) đều gặp phải đủ trường hợp phức tạp như 3 trường hợp trên. AST giải quyết vấn đề gọn gàng đơn giản hơn nhiều.

+

Dùng AST biến nhỏ thành lớn

+

Viết 4 trường hợp thành 5 dòng code. Trong 5 dòng này, ta chỉ muốn dòng 2 bị thay đổi.

+
body = """1+1<<2
+1+1<2
+2+2==3
+'1111<2'
+  #1+1<2
+"""
+
+ +

Dump AST ra màn hình:

+
>>> tree = ast.parse(body)
+>>> print(ast.dump(tree, indent=4))
+Module(
+    body=[
+        Expr(
+            value=BinOp(
+                left=BinOp(
+                    left=Constant(value=1),
+                    op=Add(),
+                    right=Constant(value=1)),
+                op=LShift(),
+                right=Constant(value=2))),
+        Expr(
+            value=Compare(
+                left=BinOp(
+                    left=Constant(value=1),
+                    op=Add(),
+                    right=Constant(value=1)),
+                ops=[
+                    Lt()],
+                comparators=[
+                    Constant(value=2)])),
+        Expr(
+            value=Compare(
+                left=BinOp(
+                    left=Constant(value=2),
+                    op=Add(),
+                    right=Constant(value=2)),
+                ops=[
+                    Eq()],
+                comparators=[
+                    Constant(value=3)])),
+        Expr(
+            value=Constant(value='1111<2'))],
+    type_ignores=[])
+
+ +

Thấy chỉ có 4 biểu thức (Expression), dòng comment không có ý nghĩa khi chạy code nên đã được bỏ qua. +AST nhận biết được dòng đầu dùng LShift (binary left shift) và Expr cuối là 1 string constant. +Duyệt qua từng Expr trong tree.body cho tới khi thấy Compare dùng ops[0] kiểu Lt (less than - nhỏ hơn) thì biến thành Gt (greater than - lớn hơn).

+
for expr in tree.body:
+    if isinstance(expr.value, ast.Compare) and isinstance(expr.value.ops[0], ast.Lt):
+        expr.value.ops[0] = ast.Gt()
+print(ast.unparse(tree))
+
+ +

Kết quả

+
1 + 1 << 2
+1 + 1 > 2
+2 + 2 == 3
+'1111<2'
+
+ +

nhỏ đã thành to như mong ước.

+

Biến đổi AST với Transformer

+

Không phải GenAI Transformer deep learning architecture mà là NodeTransformer, class giúp biến đổi Node.

+
tree = ast.parse(body)
+class RewriteOp(ast.NodeTransformer):
+
+    def visit_Lt(self, node):
+        return ast.Gt()
+
+node = RewriteOp().visit(tree)
+print(ast.unparse(node))
+
+ +

Kết quả

+
1 + 1 << 2
+1 + 1 > 2
+2 + 2 == 3
+'1111<2'
+
+ +

khi visit duyệt tới node Lt, method visit_Lt được gọi, trả về một node mới thay cho node Lt cũ, ở đây thay bằng node Gt.

+

Dùng unparse để biến AST thành code. Code này có AST tương đương với code viết tay, nhưng không giống hệt, không có comment.

+

Chạy online tại đây https://glot.io/snippets/h0vnmlw2nm

+

Kết luận

+

AST giúp check và biến đổi code dễ dàng khi dùng text là muôn vàn khó khăn.

+

Hết.

+

HVN at http://pymi.vn and https://www.familug.org.

+

Ủng hộ tác giả 🍺

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/author/pymier0.html b/author/pymier0.html new file mode 100644 index 0000000..e69de29 diff --git a/author/pymier010.html b/author/pymier010.html new file mode 100644 index 0000000..e69de29 diff --git a/author/pymier02.html b/author/pymier02.html new file mode 100644 index 0000000..e69de29 diff --git a/author/pymier03.html b/author/pymier03.html new file mode 100644 index 0000000..e69de29 diff --git a/author/pymier04.html b/author/pymier04.html new file mode 100644 index 0000000..e69de29 diff --git a/author/pymier05.html b/author/pymier05.html new file mode 100644 index 0000000..e69de29 diff --git a/author/pymier06.html b/author/pymier06.html new file mode 100644 index 0000000..e69de29 diff --git a/author/pymier07.html b/author/pymier07.html new file mode 100644 index 0000000..e69de29 diff --git a/author/pymier08.html b/author/pymier08.html new file mode 100644 index 0000000..e69de29 diff --git a/author/pymier09.html b/author/pymier09.html new file mode 100644 index 0000000..e69de29 diff --git a/author/tung491.html b/author/tung491.html new file mode 100644 index 0000000..e69de29 diff --git a/authors.html b/authors.html new file mode 100644 index 0000000..e69de29 diff --git a/base16.html b/base16.html new file mode 100644 index 0000000..c29dec0 --- /dev/null +++ b/base16.html @@ -0,0 +1,195 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Base16 là gì? base là gì? +

+
+

+ by Pymier0 +

+
+
+
+

img

+

Bài trước giới thiệu Base64 vì nó sử dụng 64 ký tự A-Za-z0-9+/ để biểu diễn binary data tùy ý. +Ngoài Base64, trong tài liệu định nghĩa Base64 còn nhắc tới: Base16, Base32, and Base64.

+

Base16

+

Base16 sử dụng 16 ký tự để biểu diễn binary: 0-9a-f (hoặc viết hoa 0-9A-F) hay còn có tên gọi phổ biến là hex. Mỗi 8bits (1byte) sẽ được chia làm 2 phần 4 bits, mỗi 4 bits có khả năng biểu diễn 2**4 == 16 giá trị.

+
>>> hex(2022)
+'0x7e6'
+>>> bin(2022)
+'0b11111100110'
+
+ +

Chia bit ra thành các nhóm 4:

+

111-1110-0110’ +111 chưa đủ 4, thêm số 0 vào trước không thay đổi ý nghĩa.

+
    +
  • 0111 là 7
  • +
  • 1110 là e
  • +
  • 0110 là 6
  • +
+

hex của giá trị số nguyên 2022 là 7e6.

+

Để đổi từ string biểu diễn hex về int, dùng int

+
>>> int('0x7e6', 16)
+2022
+
+ +

Chú ý function hex chỉ nhận đầu vào là số int. +Trong khi Base16 có thể biểu diễn mọi giá trị, ta cần dùng thư viện base64:

+
>>> base64.b16encode(b'PyMi.vn')
+b'50794D692E766E'
+>>> base64.b16decode(b'50794D692E766E')
+b'PyMi.vn'
+
+ +

Các function này nhận đầu vào là các bytes, nên không cho số vào trực tiếp được:

+
>>> base64.b16encode(2022)
+Traceback (most recent call last):
+  File "<stdin>", line 1, in <module>
+  File "/usr/lib/python3.8/base64.py", line 250, in b16encode
+    return binascii.hexlify(s).upper()
+TypeError: a bytes-like object is required, not 'int'
+
+ +

Có thể đổi từ int qua byte bằng method .to_bytes()

+
>>> base64.b16encode((2022).to_bytes(2, 'big'))
+b'07E6'
+>>> base64.b16encode((2022).to_bytes(2, 'little'))
+b'E607'
+
+ +
 |  to_bytes(self, /, length, byteorder, *, signed=False)
+ |      Return an array of bytes representing an integer.
+
+ +

big’ hay ‘little’ là viết tắt của big-endian, và little-endian, thứ tự sắp xếp các byte trên máy tính.

+

Để kiểm tra máy mình dùng order nào gõ:

+
>>> import sys
+>>> sys.byteorder
+'little'
+
+ +
>>> base64.b16encode((2022).to_bytes(2, 'big'))
+b'07E6'
+>>> base64.b16encode((2022).to_bytes(2, 'little'))
+b'E607'
+
+ +

Thứ tự giống như việc người Việt đọc truyện tranh từ trái qua phải thì người Nhật đọc từ phải qua trái, không có kiểu nào là “sai” cả. Trên máy tính cũng vậy, dùng little hay big đều không “sai”.

+

2022 khi viết thành dạng binary: ‘111-1110-0110’

+
    +
  • 0111 là 7
  • +
  • 1110 là e
  • +
  • 0110 là 6
  • +
+

Sử dụng little-endian, ta viết phần nhỏ trước, mỗi lần lấy 1 byte (== 8bits == 2 nhóm 4 bits), vậy có E6, rồi 07 => E607.

+

PS: bitcoin cũng dùng little-endian

+

Ở đây thấy chút trái ngược, vậy 2022 là E607 hay 07E6?

+

Cả 2 đều đúng, phụ thuộc vào việc đang sử dụng big hay little endian. Python int, hex và số (literal) dùng big endian.

+
>>> 0x7e6
+2022
+>>> 0x07e6
+2022
+
+ +

Base

+
>>> help(int)
+int(x, base=10) -> integer
+
+ +

Base là hệ cơ số, hệ nhị phân là base 2, hệ bát phân (oct) là base 8, hệ thập phân (decimal) là base 10, hex là hệ base 16.

+

Hết.

+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/base64.html b/base64.html new file mode 100644 index 0000000..2b85141 --- /dev/null +++ b/base64.html @@ -0,0 +1,400 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Base64 là gì? tại sao lại là 64? +

+
+

+ by Pymier0 +

+
+
+
+

img

+

Khi lập trình, hay làm sysadmin, một khái niệm đâu đó với tên encode Base64 xuất hiện và không quá khó để dùng:

+

Lệnh trên Ubuntu Linux:

+
echo -n PyMi.vn | base64
+UHlNaS52bg==
+
+ +

Hay Python3:

+
>>> import base64
+>>> base64.b64encode("PyMi.vn")
+Traceback (most recent call last):
+  File "<stdin>", line 1, in <module>
+  File "/usr/lib/python3.8/base64.py", line 58, in b64encode
+    encoded = binascii.b2a_base64(s, newline=False)
+TypeError: a bytes-like object is required, not 'str'
+>>> base64.b64encode(b"PyMi.vn")
+b'UHlNaS52bg=='
+
+ +

một cách nhận diện dữ liệu ở dạng Base64 là gồm ký tự từ A-Z a-z 0-9 và thường kết thúc với một vài dấu =.

+

Nôm na thì đơn giản, chi tiết hơn sẽ có nhiều điều thú vị hay ho rất chi này nọ.

+

Base64 cho phép biến dữ liệu binary thành dạng text (ASCII). Ví dụ như 1 file ảnh, logo Python, khi viết HTML có thể dùng thẻ img và đường link:

+
<img src="https://www.python.org/static/img/python-logo.png">
+
+ +

Nhưng cũng có thể biến file ảnh này dạng text và viết:

+
<img src="data:image/png;base64,BASE64OFIMAGE">
+
+ +

viết kiểu này có ưu điểm không bị phụ thuộc vào việc link ảnh sống hay chết, vì toàn bộ bức ảnh đã nằm ngay trong file HTML. Chuột phải và copy link của bức ảnh sau để thấy:

+

+

Với Base64 thu được khi tải file logo và chạy

+
base64 python-logo.png
+
+ +

Base64 được dùng phổ biến khi dùng email với từ khóa MIME, để nhúng ảnh, gửi kèm file với email.

+

Base64 là gì

+

Theo tài liệu định nghĩa Base64 RFC3548:

+

The Base 64 encoding is designed to represent arbitrary sequences of +octets in a form that requires case sensitivity but need not be +humanly readable.

+

Tại sao lại là 64?

+

Vì việc encoding sử dụng 64 ký tự (+1 ký tự = để padding).

+

64 ký tự gồm:

+
    +
  • 26 ký tự A-Z
  • +
  • 26 ký tự a-z
  • +
  • 10 ký tự 0-9
  • +
  • +/ (hoặc thay bằng -_ khi encode URL)
  • +
+

Cách encode

+

Biểu diễn nhóm 24-bit đầu vào thành 4 ký tự đầu ra.

+

24 bit tạo bởi 3 byte (3 * 8 == 24) đặt cạnh nhau từ trái qua phải, sau đó chia làm 4 phần 6 bits, mỗi phần đổi bit ra số rồi tra trong bảng xem ứng với ký tự nào. Do mỗi phần có 6 bit nên có khả năng biểu diễn 2**6 = 64 ký tự.

+
+--first octet--+-second octet--+--third octet--+
+|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|
++-----------+---+-------+-------+---+-----------+
+|5 4 3 2 1 0|5 4 3 2 1 0|5 4 3 2 1 0|5 4 3 2 1 0|
++--1.index--+--2.index--+--3.index--+--4.index--+
+
+ +

Đầu vào luôn cần là bội của 6 bits, nên nếu thiếu ta cần thêm vào đó các bit 0 +cho đủ. Ví dụ có 4 ký tự đầu vào = 8 * 4 == 32 bits - (6 bits * 5) = 2 bits. 2 bits nên +phải thêm 4 bit 0 vào sau cho đủ 6 bit.

+

Mỗi 24 bits đầu vào sinh ra 4 ký tự Base64, nên đầu ra nếu thiếu ký tự cần thêm các ký tự = cho đủ bội của 4 phục vụ việc decode - chuyển ngược lại từ Base64 thành binary.

+

Code Python để encode Base64

+

Python có sẵn thư viện base64 với function b64encode để biến bytes đầu vào thành dạng text Base64.

+

Code sau code theo mô tả cách thực hiện Base64 ở trên:

+
import string
+b64 = string.ascii_uppercase + string.ascii_lowercase + string.digits + '+/'
+
+data = 'PyMi.vn'.encode('utf-8')
+bits = []
+for c in data:
+    print(type(c), c, bin(c))
+    bits.extend(bin(c)[2:].zfill(8))
+
+output = []
+for i24 in range(0, len(bits), 24):
+    g1 = bits[i24:i24+24]
+    for i in range(0, len(g1), 6):
+        six_bits = g1[i:i+6]
+        while len(six_bits) < 6:
+            six_bits.append('0')
+        n = int(''.join(six_bits), 2)
+        output.append(b64[n])
+        print(six_bits, n, b64[n])
+
+while len(output) % 4 != 0:
+    output.append('=')
+
+print(''.join(output))
+print(base64.b64encode(b'PyMi.vn'))
+
+ +

Output

+
<class 'int'> 80 0b1010000
+<class 'int'> 121 0b1111001
+<class 'int'> 77 0b1001101
+<class 'int'> 105 0b1101001
+<class 'int'> 46 0b101110
+<class 'int'> 118 0b1110110
+<class 'int'> 110 0b1101110
+['0', '1', '0', '1', '0', '0'] 20 U
+['0', '0', '0', '1', '1', '1'] 7 H
+['1', '0', '0', '1', '0', '1'] 37 l
+['0', '0', '1', '1', '0', '1'] 13 N
+['0', '1', '1', '0', '1', '0'] 26 a
+['0', '1', '0', '0', '1', '0'] 18 S
+['1', '1', '1', '0', '0', '1'] 57 5
+['1', '1', '0', '1', '1', '0'] 54 2
+['0', '1', '1', '0', '1', '1'] 27 b
+['1', '0', '0', '0', '0', '0'] 32 g
+UHlNaS52bg==
+b'UHlNaS52bg=='
+
+ +

Tham khảo

+

https://datatracker.ietf.org/doc/html/rfc3548.html

+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/base64impl.html b/base64impl.html new file mode 100644 index 0000000..3241d0c --- /dev/null +++ b/base64impl.html @@ -0,0 +1,173 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Thư viện base64 trong Python được viết thế nào +

+
+

+ by Pymier0 +

+
+
+
+

img

+

Bài trước đã tự viết code Python để encoding từ bytes sang base64, code đơn giản, mang tính chất minh họa. Vậy stdlib base64 Python viết thế nào?

+

https://github.com/python/cpython/blob/3.10/Lib/base64.py#L51-L62

+
# Base64 encoding/decoding uses binascii
+
+def b64encode(s, altchars=None):
+    """Encode the bytes-like object s using Base64 and return a bytes object.
+    Optional altchars should be a byte string of length 2 which specifies an
+    alternative alphabet for the '+' and '/' characters.  This allows an
+    application to e.g. generate url or filesystem safe Base64 strings.
+    """
+    encoded = binascii.b2a_base64(s, newline=False)
+    if altchars is not None:
+        assert len(altchars) == 2, repr(altchars)
+        return encoded.translate(bytes.maketrans(b'+/', altchars))
+    return encoded
+
+ +

hóa ra base64 lại gọi 1 thư viện khác binascii để thực hiện việc encoding.

+
+

The binascii module contains a number of methods to convert between binary and various ASCII-encoded binary representations. Normally, you will not use these functions directly but use wrapper modules like uu, base64, or binhex instead.

+
+

The binascii module contains low-level functions written in C for greater speed that are used by the higher-level modules.

+

https://docs.python.org/3/library/binascii.html

+

Chú ý function b64encode(s, altchars=None) nhận 1 đầu vào optional altchars, là 2 ký tự thay thế +/ (thường là -_ để encode URL).

+

vậy binascii là thư viện viết bằng C, để có được tốc độ nhanh hơn.

+

Tò mò xem PyPy - Python viết bằng Python thì sao:

+
table_b2a_base64 = (
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
+
+@unwrap_spec(bin='bufferstr', newline=bool)
+def b2a_base64(space, bin, __kwonly__, newline=True):
+    "Base64-code line of data."
+
+    newlength = (len(bin) + 2) // 3
+    try:
+        newlength = ovfcheck(newlength * 4)
+    except OverflowError:
+        raise OperationError(space.w_MemoryError, space.w_None)
+    newlength += 1
+    res = StringBuilder(newlength)
+
+    leftchar = 0
+    leftbits = 0
+    for c in bin:
+        # Shift into our buffer, and output any 6bits ready
+        leftchar = (leftchar << 8) | ord(c)
+        leftbits += 8
+        res.append(table_b2a_base64[(leftchar >> (leftbits-6)) & 0x3f])
+        leftbits -= 6
+        if leftbits >= 6:
+            res.append(table_b2a_base64[(leftchar >> (leftbits-6)) & 0x3f])
+            leftbits -= 6
+    #
+    if leftbits == 2:
+        res.append(table_b2a_base64[(leftchar & 3) << 4])
+        res.append(PAD)
+        res.append(PAD)
+    elif leftbits == 4:
+        res.append(table_b2a_base64[(leftchar & 0xf) << 2])
+        res.append(PAD)
+    if newline:
+        res.append('\n')
+    return space.newbytes(res.build())
+
+ +

https://foss.heptapod.net/pypy/pypy/-/blob/branch/py3.8/pypy/module/binascii/interp_base64.py#L92-124

+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/big_float.html b/big_float.html new file mode 100644 index 0000000..bfe90dc --- /dev/null +++ b/big_float.html @@ -0,0 +1,148 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Tính căn số lớn +

+
+

+ by Pymier0 +

+
+
+
+

img

+

Trong Python, kiểu int có độ lớn tùy ý, như 2 mũ 2048:

+
>>> b = 2**2048
+>>> b
+32317006071311007300714876688669951960444102669715484032130345427524655138867890893197201411522913463688717960921898019494119559150490921095088152386448283120630877367300996091750197750389652106796057638384067568276792218642619756161838094338476170470581645852036305042887575891541065808607552399123930385521914333389668342420684974786564569494856176035326322058077805659331026192708460314150258592864177116725943603718461857357598351152301645904403697613233287231227125684710820209725157101726931323469678542580656697935045997268352998638215525166389437335543602135433229604645318478604952148193555853611059596230656
+
+ +

cộng trừ vẫn thoải mái, nhưng… tính căn 2 (hoặc chia) thì sao?

+
>>> from math import sqrt
+>>> sqrt(b)
+Traceback (most recent call last):
+  File "<stdin>", line 1, in <module>
+OverflowError: int too large to convert to float
+
+ +

Nội dung error cho thấy số này quá lớn để biến thành kiểu float (kết quả của phép chia, căn đều là float). Giải pháp là dùng math.isqrt

+
>>> from math import isqrt
+>>> isqrt(b)
+179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137216
+>>> isqrt(b) == 2**1024
+True
+
+ +

Docstring:

+
isqrt(n, /)
+    Return the integer part of the square root of the input.
+
+ +

kết quả không chắc CHÍNH XÁC là căn 2 (bởi không phải căn 2 số nào cũng là số nguyên), nên cần test lại nếu bình phương kết quả có bằng số ban đầu không.

+
>>> isqrt(10)
+3
+>>> 3 ** 2 == 10
+False
+
+>>> 2**1000/2
+5.357543035931337e+300
+>>> isqrt(2**1000) ** 2 == 2**1000
+True
+
+ +

Hết.

+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/bool-methods.html b/bool-methods.html new file mode 100644 index 0000000..493259c --- /dev/null +++ b/bool-methods.html @@ -0,0 +1,129 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Các method của boolean True False +

+
+

+ by Pymier0 +

+
+
+
+

img

+

True và False là hai giá trị kiểu bool trong Python, có thể nói thuộc một trong những thứ đơn giản nhất, vì chỉ có hai giá trị.

+

Sau khi học hành tử tế sẽ biết các operator and or not và chúng có tính short-circuit, và… hết.

+

Hỏi: giá trị boolean có các methods nào?

+

Trả lời: what?!!! đó có lẽ là thứ mà bạn chưa từng thử.

+

bool trong Python thực chất inherit từ class base là int, nó có mọi methods của int. WHAT? int cũng có method? chính xác, mọi thứ trong Python đều là object.

+
>>> dir(True) == dir(83)
+True
+>>> print(dir(83))
+['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '_class__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mpow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'as_ing', 'numerator', 'real', 'to_bytes']
+>>> True.__class__.__base__
+<class 'int'>
+
+ +

Các “public” methods:

+
>>> x.as_integer_ratio()
+(1, 1)
+>>> x.bit_length()
+1
+>>> x.to_bytes(1, 'big')
+b'\x01'
+
+ +

Hết.

+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/build.html b/build.html new file mode 100644 index 0000000..d1355a3 --- /dev/null +++ b/build.html @@ -0,0 +1,153 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Build Python từ source trên Ubuntu 20.04 +

+
+

+ by Pymier0 +

+
+
+
+

Build phần mềm từ source dù là chuyện phổ biến trong giới mã nguồn mở từ xưa nhưng dần dần trở thành bí kíp thất +truyền với việc các package manager (như apt, yum, …) đều cài sẵn binary (sản phẩm của việc build).

+

Build từ source không tiện lợi cho mục đích cài phần mềm hàng ngày, nhưng là một kiến thức tốt trong học tập và nghiên cứu.

+

Build CPython bản mới nhất trên Ubuntu cũng không có khó khăn gì, mất khoảng 3-5 phút tùy tốc độ máy tính.

+

img

+

Chuẩn bị

+

Để thực hiện bài này, cần có 4 phần mềm:

+
    +
  • curl để tải file source của Python
  • +
  • tar để giải nén
  • +
  • gcc có C compiler - để build
  • +
  • make để chạy lệnh build
  • +
+

Chạy trên Ubuntu 20.04.3 LTS

+

Cài:

+
sudo apt update && sudo apt-get install -y curl make gcc tar
+
+ +

Bắt đầu

+

Tải từ trang chủ https://www.python.org/downloads/release/python-397/

+
curl -LO https://www.python.org/ftp/python/3.9.7/Python-3.9.7.tgz
+
+ +

Giải nén

+
tar xf Python-3.9.7.tgz;  cd Python-3.9.7
+
+ +

Build - theo hướng dẫn trong file README.rst

+
    ./configure
+    make
+    # make test
+    # sudo make install
+
+ +

configure là 1 shell script, chạy các câu lệnh kiểm tra các điều kiện cần thiết (như có C compiler chưa, …) và sinh ra file Makefile.

+

make chạy lệnh trong Makefile, nếu quá trình thành công sẽ tạo ra file python.

+

Đây chính là chương trình python thu được.

+
# ./python
+Python 3.9.7 (default, Oct  3 2021, 16:21:43)
+[GCC 9.3.0] on linux
+
+ +

Sau khi xong có thể chạy thêm make test để đảm bảo python chạy thành công các test, và sudo make install để cài vào máy thay Python trên máy.

+

Trong quá trình compile, người dùng sẽ nhìn thấy các thành phần của Python nằm ở file C code nào - không nhất thiết phải biết code C, nhưng ít ra biết nó ở đâu. Ví dụ:

+
gcc -pthread -c -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall    -std=c99 -Wextra -Wno-unused-result -Wno-unused-parameter -Wno-missing-field-initializers -Werror=implicit-function-declaration -fvisibility=hidden  -I./Include/internal  -I. -I./Include    -DPy_BUILD_CORE -o Objects/listobject.o Objects/listobject.c
+
+ +

Hết.

+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/builtins.html b/builtins.html new file mode 100644 index 0000000..a5b1556 --- /dev/null +++ b/builtins.html @@ -0,0 +1,188 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Bất ngờ với __builtins__ +

+
+

+ by Pymier0 +

+
+
+
+

Python có vài chục function có sẵn, liệt kê tại https://docs.python.org/3/library/functions.html, bật lệnh python3 lên gõ là có:

+
$ python3
+Python 3.10.10 (main, Mar  5 2023, 22:26:53) [GCC 12.2.1 20230201] on linux
+Type "help", "copyright", "credits" or "license" for more information.
+>>> print(len([1,3,2]))
+3
+
+ +

Định nghĩa 1 function rồi gọi globals để liệt kê tất cả các “tên” có thể truy cập được:

+
>>> def double(x):
+...     return x*2
+...
+>>> n = 42
+>>> globals()
+{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'double': <function double at 0x7f729f6065f0>, 'n': 42}
+>>> help(globals)
+globals()
+    Return the dictionary containing the current scope's global variables.
+
+    NOTE: Updates to this dictionary *will* affect name lookups in the current
+    global scope and vice-versa.
+
+ +

Python interpreter interative mode có hỗ trợ “autocomplete”, gõ __ rồi bấm phím Tab, thấy:

+
>>> __
+__annotations__   __doc__           __name__
+__build_class__(  __import__(       __package__
+__debug__         __loader__()      __spec__
+
+ +

Thấy có chút khác biệt: autocomplete không hiện __builtins__, còn globals() lại không có __debug____import__, __build_class__.

+

__import__

+
__import__(...)
+    __import__(name, globals=None, locals=None, fromlist=(), level=0) -> module
+
+    Import a module. Because this function is meant for use by the Python
+    interpreter and not for general use, it is better to use
+    importlib.import_module() to programmatically import a module.
+    ...
+
+ +

Function __import__ thực hiện import khi gõ import lib.

+

__builtins__

+

__builtins__ là module builtins

+
>>> import builtins
+>>> builtins.print(builtins.len([3,2,1]))
+3
+>>> help(__builtins__)
+Help on built-in module builtins:
+
+NAME
+    builtins - Built-in functions, exceptions, and other objects.
+    ...
+
+ +

nơi chứa các builtin functions như print, len, …

+
>>> [i for i in dir(__builtins__) if not i.startswith("__") and i.islower()]
+['abs', 'aiter', 'all', 'anext', 'any', 'ascii', 'bin', 'bool', 'breakpoint', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']
+
+ +

Trong tài liệu viết

+
+

CPython implementation detail: Users should not touch builtins; it is strictly an implementation detail. Users wanting to override values in the builtins namespace should import the builtins module and modify its attributes appropriately.

+
+

should not không có nghĩa là cannot, thử gán cho __builtins__ một module khác:

+
>>> import os
+>>> __builtins__ = os
+>>> getcwd()
+'/home/hvn'
+>>> globals()
+Traceback (most recent call last):
+  File "<stdin>", line 1, in <module>
+NameError: name 'globals' is not defined
+>>> print
+Traceback (most recent call last):
+  File "<stdin>", line 1, in <module>
+NameError: name 'print' is not defined
+
+ +

các builtin function như print hay globals không còn nữa, thay vào đó là các funtion trong module os.

+

Bất ngờ chưa?!

+

Kết luận

+

Python vẫn luôn có những bí mật rất không muốn bật mí.

+

Hết.

+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/byt351.html b/byt351.html new file mode 100644 index 0000000..feae969 --- /dev/null +++ b/byt351.html @@ -0,0 +1,208 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ [hack] Biến đổi giữa các kiểu số thành byte +

+
+

+ by Pymier0 +

+
+
+
+

img

+

Hệ thập phân (hệ số 10) là hệ số dùng phổ biến nhất ở loài người, 1,2,3,4,5,6,7,8,9…0 đều học +từ lớp 1 2 3.

+

Máy tính biểu diễn mọi thứ ở hệ nhị phân (hệ số 2) với số 0 và 1, gọi là binary.

+

Trên máy tính còn dùng các hệ khác như octal, hex và chúng đều có thể biểu diễn +chung ở 1 dạng gọi là byte.

+

bit

+

bit là đơn vị nhỏ nhất trên máy tính, bit có thể có 2 giá trị 0 và 1. Số khi +biểu diễn ở dạng bit viết thành binary: 10010101011

+

Trên Python, có thể viết 0b rồi gõ giá trị, ví dụ: 0b101010 là dạng binary của số 42.

+
>>> 0b101010 == 42
+True
+
+ +

Có thể biến 42 thành str (string) biểu diễn dạng binary với function bin:

+
>>> bin(42)
+'0b101010'
+
+ +

Và biến ngược lại từ str thành số 10:

+
>>> int('101010', base=2)
+42
+
+ +

byte

+

Vì bit quá nhỏ, để viết được số 255 cần tới 8 bit: 11111111 rất dài dòng và +tốn giấy mực, nên người ta đổi 8 bits thành 1 byte. 1 byte có thể biểu diễn +bất kỳ giá trị trong khoảng 0-255 (2 mũ 8).

+
>>> x = 254
+>>> x.bit_length()
+8
+>>> x.to_bytes(length=1, byteorder='little')
+b'\xfe'
+>>> x.to_bytes(length=1, byteorder='big')
+b'\xfe'
+
+ +

bytes là kiểu dữ liệu trên python để biểu diễn các… byte. bytes được ký hiệu bằng chữ +b trước nội dung tương tự kiểu string.

+
>>> b'python'
+b'python'
+>>> type(b'python')
+<class 'bytes'>
+>>> [i for i in b'python']
+[112, 121, 116, 104, 111, 110]
+>>> [ord(c) for c in 'python']
+[112, 121, 116, 104, 111, 110]
+
+ +

Khi duyệt qua từng byte, chúng là các giá trị số int.

+

Khi in ra, các giá trị không có dạng ký tự biểu diễu như 0 1 2 hay a b c ?<>.,+ … +sẽ được biễu diễn bằng ký hiệu b’\x1f` với 1f là dạng hex của byte này.

+
>>> bytes(range(256))
+b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff'
+
+ +

hex

+

byte là đơn vị máy tính sử dụng, thì hex là giá trị +con người sử dụng, để dễ dàng biểu diễn byte. +Hex là hệ gồm 16 số 0123456789abcdef, với a = 10, b = 11,… f = 15. +Có thể thấy một sự trùng hợp không hề ngẫu nhiên:

+
    +
  • 256 == 2 mũ 8
  • +
  • 16 == 2 mũ 4
  • +
  • Các giá trị từ 0 đến 255 có thể biểu diễn ở dạng hex với 2 ký tự.
  • +
+

Trên Python:

+
>>> 0xfe == 254
+True
+
+ +

Đổi từ số sang hex string, và ngược lại:

+
>>> hex(254)
+'0xfe'
+>>> int('fe', base=16)
+254
+
+ +

đổi byte sang hex

+
>>> x = b'python'
+>>> x.hex()
+'707974686f6e'
+>>> bytes.fromhex('707974686f6e')
+b'python'
+
+ +

Giải 1 bài trong giải CTF imaginaryctf 2022:

+

Đoạn ký tự này mã hóa cái gì?

+
s = '👎👍👍👎👍👎👎👍👎👍👍👎👎👎👍👍👎👍👍👍👎👍👎👎👎👍👍👎👎👍👍👎👎👍👍👍👍👎👍👍👎👍👍👎👎👍👎👍👎👍👍👎👍👍👍👎👎👍👍👎👎👎👍👍👎👎👍👍👎👎👎👎👎👍👍👎👎👍👎👎👎👍👍👎👍👎👎👍👎👍👍👎👍👍👍👎👎👍👍👎👎👍👍👍👎👍👎👍👍👍👍👍👎👍👍👎👍👎👎👍👎👍👍👍👎👎👍👍👎👍👎👍👍👍👍👍👎👍👍👎👍👍👍👎👎👎👍👍👎👎👎👎👎👍👍👍👎👍👎👎👎👍👎👍👍👍👍👍👎👍👍👎👎👍👎👍👎👍👍👎👍👍👍👎👎👍👍👎👎👎👍👍👎👍👍👍👎👎👍👎👎👍👍👍👍👎👎👍👎👍👍👍👎👎👎👎👎👍👍👍👎👍👎👎👎👍👍👎👍👎👎👍👎👎👍👍👎👎👎👎👎👍👍👎👍👍👍👎👎👍👎👍👍👍👍👍👎👎👍👍👎👎👎👍👎👍👍👎👎👎👍👎👎👎👍👍👎👎👍👎👎👍👍👎👎👍👎👍👎👎👍👍👎👎👎👎👎👍👍👎👎👍👎👎👎👎👍👍👎👍👎👎👎👎👍👍👎👎👍👍👎👍👍👍👍👍👎👍'
+
+ +

Đoán 1 chút, dấu 👎 đại diện cho số 0, dấu 👍 đại diện cho số 1, +biến thành string 0, 1 rồi đổi thành số hệ 10, sau đó đổi sang hex, và biến thành bytes:

+
b = ''.join('1' if i == '👍' else '0' for i in s)
+print(b)
+# 0110100101100011011101000110011001111011011001010110111001100011001100000110010001101001011011100110011101011111011010010111001101011111011011100011000001110100010111110110010101101110011000110111001001111001011100000111010001101001001100000110111001011111001100010110001000110010011001010011000001100100001101000011001101111101
+bytes.fromhex(hex(int(b, 2))[2:])
+# b'ictf{enc0ding_is_n0t_encrypti0n_1b2e0d43}'
+
+ +

Thấy thú zị??? tham khảo thêm các writeup của Pymi tại https://github.com/pymivn/ctf

+

Tham gia team CTF PYMI qua Slack pymi #ctf nha

+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/categories.html b/categories.html new file mode 100644 index 0000000..9a6f009 --- /dev/null +++ b/categories.html @@ -0,0 +1,72 @@ + + + + Tin tức Python PyMI.vn - Categories + + + + + + + + + + + + + + + + + +
+
+

categories

+
+
+ +
+
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/category/features.html b/category/features.html new file mode 100644 index 0000000..06cf0e5 --- /dev/null +++ b/category/features.html @@ -0,0 +1,372 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + +
+
+

features

+
+
+
    +
  1. + +
  2. +
  3. + +
  4. +
  5. + +
  6. +
  7. + +
  8. +
  9. + +
  10. +
  11. + +
  12. +
  13. + +
  14. +
  15. + +
  16. +
  17. + +
  18. +
  19. + +
  20. +
+

+ 1 / 2 + +

+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/category/features2.html b/category/features2.html new file mode 100644 index 0000000..24626e0 --- /dev/null +++ b/category/features2.html @@ -0,0 +1,103 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + +
+
+

features

+
+
+
    +
  1. + +
  2. +
+

+ + 2 / 2 +

+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/category/misc.html b/category/misc.html new file mode 100644 index 0000000..fb6ab0a --- /dev/null +++ b/category/misc.html @@ -0,0 +1,101 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + +
+
+

misc

+
+
+
    +
  1. + +
  2. +
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/category/news.html b/category/news.html new file mode 100644 index 0000000..6b70d8e --- /dev/null +++ b/category/news.html @@ -0,0 +1,361 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + +
+
+

news

+
+
+
    +
  1. + +
  2. +
  3. + +
  4. +
  5. + +
  6. +
  7. + +
  8. +
  9. + +
  10. +
  11. + +
  12. +
  13. + +
  14. +
  15. + +
  16. +
  17. + +
  18. +
  19. + +
  20. +
+

+ 1 / 7 + +

+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/category/news2.html b/category/news2.html new file mode 100644 index 0000000..9c7a0cc --- /dev/null +++ b/category/news2.html @@ -0,0 +1,366 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + +
+
+

news

+
+
+
    +
  1. + +
  2. +
  3. + +
  4. +
  5. + +
  6. +
  7. + +
  8. +
  9. + +
  10. +
  11. + +
  12. +
  13. + +
  14. +
  15. + +
  16. +
  17. + +
  18. +
  19. + +
  20. +
+

+ + 2 / 7 + +

+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/category/news3.html b/category/news3.html new file mode 100644 index 0000000..523b56e --- /dev/null +++ b/category/news3.html @@ -0,0 +1,367 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + +
+
+

news

+
+
+
    +
  1. + +
  2. +
  3. + +
  4. +
  5. + +
  6. +
  7. + +
  8. +
  9. + +
  10. +
  11. + +
  12. +
  13. + +
  14. +
  15. + +
  16. +
  17. + +
  18. +
  19. + +
  20. +
+

+ + 3 / 7 + +

+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/category/news4.html b/category/news4.html new file mode 100644 index 0000000..a3e7e4e --- /dev/null +++ b/category/news4.html @@ -0,0 +1,363 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + +
+
+

news

+
+
+
    +
  1. + +
  2. +
  3. + +
  4. +
  5. + +
  6. +
  7. + +
  8. +
  9. + +
  10. +
  11. + +
  12. +
  13. + +
  14. +
  15. + +
  16. +
  17. + +
  18. +
  19. + +
  20. +
+

+ + 4 / 7 + +

+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/category/news5.html b/category/news5.html new file mode 100644 index 0000000..4e16448 --- /dev/null +++ b/category/news5.html @@ -0,0 +1,364 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + +
+
+

news

+
+
+
    +
  1. + +
  2. +
  3. + +
  4. +
  5. + +
  6. +
  7. + +
  8. +
  9. + +
  10. +
  11. + +
  12. +
  13. + +
  14. +
  15. + +
  16. +
  17. + +
  18. +
  19. + +
  20. +
+

+ + 5 / 7 + +

+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/category/news6.html b/category/news6.html new file mode 100644 index 0000000..56e405b --- /dev/null +++ b/category/news6.html @@ -0,0 +1,358 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + +
+
+

news

+
+
+
    +
  1. + +
  2. +
  3. + +
  4. +
  5. + +
  6. +
  7. + +
  8. +
  9. + +
  10. +
  11. + +
  12. +
  13. + +
  14. +
  15. + +
  16. +
  17. + +
  18. +
  19. + +
  20. +
+

+ + 6 / 7 + +

+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/category/news7.html b/category/news7.html new file mode 100644 index 0000000..24fff6a --- /dev/null +++ b/category/news7.html @@ -0,0 +1,289 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + +
+
+

news

+
+
+
    +
  1. + +
  2. +
  3. + +
  4. +
  5. + +
  6. +
  7. + +
  8. +
  9. + +
  10. +
  11. + +
  12. +
  13. + +
  14. +
  15. + +
  16. +
+

+ + 7 / 7 +

+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/category/pymivn.html b/category/pymivn.html new file mode 100644 index 0000000..cb15e3a --- /dev/null +++ b/category/pymivn.html @@ -0,0 +1,366 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + +
+
+

pymi.vn

+
+
+
    +
  1. + +
  2. +
  3. + +
  4. +
  5. + +
  6. +
  7. + +
  8. +
  9. + +
  10. +
  11. + +
  12. +
  13. + +
  14. +
  15. + +
  16. +
  17. + +
  18. +
  19. + +
  20. +
+

+ 1 / 2 + +

+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/category/pymivn2.html b/category/pymivn2.html new file mode 100644 index 0000000..45a10f5 --- /dev/null +++ b/category/pymivn2.html @@ -0,0 +1,162 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + +
+
+

pymi.vn

+
+
+
    +
  1. + +
  2. +
  3. + +
  4. +
  5. + +
  6. +
+

+ + 2 / 2 +

+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/compile.html b/compile.html new file mode 100644 index 0000000..7a7d5e5 --- /dev/null +++ b/compile.html @@ -0,0 +1,165 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ CPython compiler +

+
+

+ by Pymier0 +

+
+
+
+

img

+

Python thường được biết tới như một ngôn ngữ lập trình scripting/interpreted. +Lập trình viên sau khi viết code xong, chỉ cần gõ python tên_file.py để chạy +code dẫn tới một sự hiểu nhầm phổ biến rằng “python không compile code”.

+

Để tránh các tranh cãi không cần thiết, ở đây chỉ nói tới CPython - tức bản +Python phổ biến nhất mà gần như tất cả mọi người đều dùng, tải từ +python.org.

+

Để chạy 1 file code Python, CPython có bước compile code, nhưng bước này +CPython tự thực hiện mà không cần lập trình viên phải thực hiện. Việc compile +này cũng thường diễn ra rất nhanh chóng nên khó có thể phát hiện ra. Khác với +C/C++/Golang, Python compile code không sinh ra 1 file binary chạy được. Giống +với Java, Python sinh ra bytecode, bytecode này được chạy bởi Python virtual +machine (với Java là JVM).

+

Dễ quan sát hơn khi sử dụng module, CPython mặc định sẽ sinh ra file .pyc +chứa compiled bytecode. Tạo 1 file tên mylib.py và 1 file main main.py

+
# main.py
+import mylib
+print(mylib.double(21))
+
+ # mylib.py
+def double(x):
+    return x * 2
+
+ +

chạy file main.py

+
$ ls -la
+total 16
+drwxrwxr-x 2 hvn hvn 4096 Aug 28 20:39 .
+drwxrwxr-x 3 hvn hvn 4096 Aug 28 20:39 ..
+-rw-rw-r-- 1 hvn hvn   37 Aug 28 20:39 main.py
+-rw-rw-r-- 1 hvn hvn   32 Aug 28 20:39 mylib.py
+$ python3 main.py
+42
+$ ls -la
+total 20
+drwxrwxr-x 3 hvn hvn 4096 Aug 28 20:39 .
+drwxrwxr-x 3 hvn hvn 4096 Aug 28 20:39 ..
+-rw-rw-r-- 1 hvn hvn   37 Aug 28 20:39 main.py
+-rw-rw-r-- 1 hvn hvn   32 Aug 28 20:39 mylib.py
+drwxrwxr-x 2 hvn hvn 4096 Aug 28 20:39 __pycache__
+$ ls -la __pycache__
+total 12
+drwxrwxr-x 2 hvn hvn 4096 Aug 28 20:39 .
+drwxrwxr-x 3 hvn hvn 4096 Aug 28 20:39 ..
+-rw-rw-r-- 1 hvn hvn  235 Aug 28 20:39 mylib.cpython-38.pyc
+
+ +

Python sẽ đọc các file .pyc này mà bỏ qua bước compile ở lần chạy sau (nếu code + không thay đổi), tiết kiệm được thời gian compile, NHƯNG SAU ĐÓ CODE +CHẠY KHÔNG NHANH HƠN lần trước.

+

Để xem nội dung bytecode sinh ra từ code, dùng standard lib dis

+

Tham khảo

+ +

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/compound.html b/compound.html new file mode 100644 index 0000000..8eed202 --- /dev/null +++ b/compound.html @@ -0,0 +1,228 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Compound Interest +

+
+

+ by Pymier0 +

+
+
+
+
+

1% lãi mỗi năm, thì bao giờ tiền gửi sẽ gấp đôi?

+
+

Câu trả lời sai dễ dàng là 100 năm, vì mỗi năm là 1%, 100 * 1 == 100.

+

img

+

Điều kỳ diệu ở đây là phần lãi 1% này được gộp vào để tính lãi sau năm đầu tiên. +Năm thứ 2 lãi là 1% * (100+1), con số rất nhỏ này tăng lên rất nhanh theo thời gian.

+

Khái niệm này phổ biến với tên “compound interest” (lãi kép/ lãi gộp) +trong tài chính, khiến +người giàu càng giàu hơn, người nợ ngân hàng thì ngày càng nợ nhiều hơn (hello thẻ tín dụng).

+

Một vòng for đơn giản đủ để thấy sự kỳ diệu này, trước hết là vài con số:

+
    +
  • tiền gửi sẽ gấp đôi sau 70 năm
  • +
  • sau 100 năm, tiền gửi đã là 270%, tức gấp 2.7 lần.
  • +
+
>>> m = 100
+>>> for i in range(1, 101):
+...     m = m * 1/100 + m
+...     print(i, m)
+...
+1 101.0
+2 102.01
+3 103.0301
+4 104.060401
+5 105.10100501
+6 106.1520150601
+7 107.213535210701
+8 108.28567056280801
+9 109.36852726843608
+10 110.46221254112044
+11 111.56683466653165
+12 112.68250301319696
+13 113.80932804332893
+14 114.94742132376223
+15 116.09689553699985
+16 117.25786449236985
+17 118.43044313729355
+18 119.61474756866649
+19 120.81089504435315
+20 122.01900399479668
+21 123.23919403474464
+22 124.47158597509208
+23 125.71630183484301
+24 126.97346485319144
+25 128.24319950172335
+26 129.52563149674057
+27 130.820887811708
+28 132.12909668982508
+29 133.45038765672334
+30 134.78489153329056
+31 136.13274044862348
+32 137.49406785310973
+33 138.86900853164082
+34 140.2576986169572
+35 141.6602756031268
+36 143.07687835915806
+37 144.50764714274965
+38 145.95272361417713
+39 147.4122508503189
+40 148.8863733588221
+41 150.37523709241034
+42 151.87898946333445
+43 153.3977793579678
+44 154.9317571515475
+45 156.48107472306296
+46 158.0458854702936
+47 159.62634432499652
+48 161.2226077682465
+49 162.83483384592896
+50 164.46318218438824
+51 166.10781400623213
+52 167.76889214629446
+53 169.4465810677574
+54 171.14104687843496
+55 172.8524573472193
+56 174.5809819206915
+57 176.32679173989843
+58 178.09005965729742
+59 179.87096025387038
+60 181.6696698564091
+61 183.4863665549732
+62 185.32123022052292
+63 187.17444252272816
+64 189.04618694795545
+65 190.936648817435
+66 192.84601530560937
+67 194.77447545866548
+68 196.72222021325214
+69 198.68944241538466
+70 200.67633683953852
+71 202.6831002079339
+72 204.70993121001325
+73 206.75703052211338
+74 208.8246008273345
+75 210.91284683560787
+76 213.02197530396396
+77 215.15219505700358
+78 217.30371700757362
+79 219.47675417764935
+80 221.67152171942584
+81 223.8882369366201
+82 226.1271193059863
+83 228.38839049904615
+84 230.6722744040366
+85 232.97899714807699
+86 235.30878711955776
+87 237.66187499075335
+88 240.03849374066087
+89 242.43887867806748
+90 244.86326746484815
+91 247.31190013949663
+92 249.7850191408916
+93 252.28286933230052
+94 254.8056980256235
+95 257.35375500587975
+96 259.92729255593855
+97 262.5265654814979
+98 265.1518311363129
+99 267.80334944767606
+100 270.48138294215283
+
+ +

Hết.

+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/compound2.html b/compound2.html new file mode 100644 index 0000000..6b363bb --- /dev/null +++ b/compound2.html @@ -0,0 +1,223 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Gửi 100 triệu vào ngân hàng hôm nay, bao giờ có 1 tỷ? +

+
+

+ by Pymier0 +

+
+
+
+

img

+

Bài trước đã giới thiệu khái niệm lãi gộp (compound interest) +với lãi suất 1%. +Lãi suất ngân hàng Vietcombank tại thời điểm viết bài là 5.5%/năm, sau 13 năm +sẽ gấp đôi, sau 44 năm +ta sẽ có 1 tỷ (gấp 10), sau 100 năm sẽ có 21 tỷ.

+

img

+
rate = 5.5/100
+m = 100
+acc = []
+for i in range(1, 101):
+    m = m + m * rate
+    print(i, m)
+    acc.append(m)
+
+1 105.5
+2 111.3025
+3 117.4241375
+4 123.8824650625
+5 130.6960006409375
+6 137.88428067618906
+7 145.46791611337946
+8 153.46865149961533
+9 161.90942733209417
+10 170.81444583535935
+11 180.20924035630412
+12 190.12074857590085
+13 200.57738974757538
+14 211.60914618369202
+15 223.24764922379507
+16 235.5262699311038
+17 248.4802147773145
+18 262.1466265900668
+19 276.56469105252046
+20 291.7757490604091
+21 307.82341525873164
+22 324.7537030979619
+23 342.6151567683498
+24 361.458990390609
+25 381.3392348620925
+26 402.3128927795076
+27 424.44010188238053
+28 447.7843074859115
+29 472.4124443976366
+30 498.3951288395066
+31 525.8068609256794
+32 554.7262382765917
+33 585.2361813818043
+34 617.4241713578035
+35 651.3825007824827
+36 687.2085383255193
+37 725.0050079334228
+38 764.8802833697611
+39 806.9486989550979
+40 851.3308773976283
+41 898.1540756544979
+42 947.5525498154952
+43 999.6679400553475
+44 1054.6496767583915
+45 1112.655408980103
+46 1173.8514564740085
+47 1238.4132865800789
+48 1306.5260173419833
+49 1378.3849482957924
+50 1454.196120452061
+51 1534.1769070769244
+52 1618.5566369661553
+53 1707.5772519992938
+54 1801.494000859255
+55 1900.576170906514
+56 2005.1078603063725
+57 2115.388792623223
+58 2231.7351762175
+59 2354.4806109094625
+60 2483.9770445094828
+61 2620.595781957504
+62 2764.728549965167
+63 2916.7886202132513
+64 3077.21199432498
+65 3246.458654012854
+66 3425.013879983561
+67 3613.3896433826567
+68 3812.126073768703
+69 4021.7930078259815
+70 4242.99162325641
+71 4476.356162535512
+72 4722.555751474965
+73 4982.296317806088
+74 5256.322615285423
+75 5545.420359126121
+76 5850.418478878058
+77 6172.191495216351
+78 6511.66202745325
+79 6869.803438963178
+80 7247.642628106153
+81 7646.262972651992
+82 8066.807436147851
+83 8510.481845135982
+84 8978.558346618462
+85 9472.379055682477
+86 9993.359903745013
+87 10542.994698450988
+88 11122.859406865793
+89 11734.616674243412
+90 12380.0205913268
+91 13060.921723849773
+92 13779.27241866151
+93 14537.132401687893
+94 15336.674683780728
+95 16180.191791388668
+96 17070.102339915044
+97 18008.95796861037
+98 18999.450656883942
+99 20044.42044301256
+100 21146.863567378252
+
+ +

Hết.

+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/concurrent.html b/concurrent.html new file mode 100644 index 0000000..f5affb9 --- /dev/null +++ b/concurrent.html @@ -0,0 +1,186 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Giới thiệu concurrent.futures trong Python 3 +

+
+

+ by Pymier0 +

+
+
+
+

Concurrency

+

Concurrency là khái niệm chương trình thực hiện nhiều công việc cùng một +lúc (dễ thấy ở các chương trình có giao diện đồ họa: vừa hiển thị giao diện, +vừa kết nối đến trang web, hay các chương trình server phục vụ nhiều người dùng +cùng lúc).

+

Python (cũng như nhiều ngôn ngữ lập trình khác) từ xưa đã có hai cách làm phổ +biến để viết concurrent code: dùng threading hoặc multiprocessing.

+

Python 3 giới thiệu thư viện “bậc cao” dễ dùng hơn có tên concurrent.futures.

+

img

+

CPU bound & IO bound

+

bound” ở đây hiểu theo nghĩa: chương trình tốn hầu hết thời gian thực hiện +tính toán (CPU) hay chờ (hệ điều hành) đọc ghi dữ liệu, bao gồm cả kết nối +mạng (IO).

+

CPython có một giới hạn về thiết kế khiến cho khi dùng threading, chỉ 1 thread +được chạy (dùng CPU) 1 lúc (global interpreter lock) , muốn dùng nhiều CPU phải chuyển qua dùng multiprocessing. +Thread nhẹ hơn process, máy tính bình thường có thể có hàng chục hay trăm ngàn +thread nhưng +không đủ (RAM) để tạo 10_000 process. Trong Python, khi chương trình IO bound, +có thể dùng thread, khi chương trình CPU bound thì dùng multiprocessing mới có +thể tăng tốc.

+

concurrent.futures cho phép chuyển đổi giữa threading hay multiprocessing một cách +đơn giản.

+

Ví dụ

+

Tính tổng các số từ 1 đến 30 triệu, 4 lần.

+

Việc dùng concurrent.futures chỉ gồm 2 bước:

+
    +
  • tạo Thread/Process Pool Executor
  • +
  • chạy executor.map với 2 argument: function sẽ được chạy ở thread/Process, và +list chứa argument cho mỗi lần gọi function.
  • +
+

Trên máy có 8 CPU, kết qủa thấy dùng +ProcessPoolExecutor cho việc tính tóan CPU bound này nhanh gấp gần 4 lần +so với dùng ThreadPoolExecutor.

+
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
+
+import os
+import time
+
+def sumto(n):
+    r = 1
+    for i in range(1, n+1):
+        r = r + i
+    return r
+
+start = time.time()
+print(f"ThreadPoolExecutor: max_workers={os.cpu_count()}")
+executor = ThreadPoolExecutor(max_workers=os.cpu_count())
+
+for r in executor.map(sumto, [30_000_000,30_000_000,30_000_000,30_000_000]):
+    print(r)
+print(time.time()-start)
+
+
+start = time.time()
+executor = ProcessPoolExecutor(max_workers=os.cpu_count())
+
+print(f"ProcessPoolExecutor: max_workers={os.cpu_count()}")
+for r in executor.map(sumto, [30_000_000,30_000_000,30_000_000,30_000_000]):
+    print(r)
+print(time.time()-start)
+
+ +

Kết quả

+
$ python concurrent.py
+ThreadPoolExecutor: max_workers=8
+450000015000001
+450000015000001
+450000015000001
+450000015000001
+5.539357423782349
+ProcessPoolExecutor: max_workers=8
+450000015000001
+450000015000001
+450000015000001
+450000015000001
+1.5521023273468018
+
+ +

Hết

+

Tham khảo

+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/counter.html b/counter.html new file mode 100644 index 0000000..0b37e02 --- /dev/null +++ b/counter.html @@ -0,0 +1,187 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ collections.Counter có phải là dict? +

+
+

+ by Pymier0 +

+
+
+
+

Lập trình viên Python không bao giờ phải tự đếm số lần xuất hiện của mỗi phần tử, bởi có sẵn Counter:

+

Python Counter là gì

+
from collections import Counter
+c = Counter("py py thon thon py".split())
+print(c)
+# Counter({'py': 3, 'thon': 2})
+for k, v in c.items():
+    print(k, v)
+# py 3
+# thon 2
+
+ +

Thay vì tự viết:

+
d = {}
+for w in "py py thon thon py".split():
+    if w not in d:
+        d[w] = 1
+    else:
+        d[w] += 1
+
+print(d)
+# {'py': 3, 'thon': 2}
+
+ +

Counter có phải là dict?

+

Trước tiên, cần làm rõ, “là dict” có nghĩa là gì?

+
    +
  • Là kế thừa từ dict?
  • +
  • Là mọi method giống dict? và có thêm vài method khác
  • +
+

Dễ thấy, Counter kế thừa từ dict, nên 1 Counter, theo nghĩa của lập trình hướng đối tượng - OOP, là 1 dict:

+
class Counter(dict):
+...
+
+>>> c = collections.Counter()
+>>> isinstance(c, dict)
+True
+
+ +

Nếu một người tin vào THUYẾT tiến hóa, thì 1 người có phải 1 con vượn? hay 1 con bò sát? hay 1 con sinh vật đơn bào? Theo OOP thì là vậy.

+

Đó là lỗ hổng trong cách tư duy OOP, những ngôn ngữ lập trình mới ngày nay không theo lối suy nghĩ này, mà dựa trên khả năng của 1 object để xét nó là gì như Go interface hay Rust trait: một object là Writer nếu nó có thể write, là Reader nếu nó có thể read.

+

Quay trờ lại câu trả lời: Counter là 1 dict, câu trả lời này có tác dụng gì? có thể dùng Counter như dict mà không có khác biệt gì? +Đôi khi không phải:

+

dict

+
print(d['py'])
+# 3
+print(d.get('secret'))
+# None
+print(d['secret'])
+# Traceback (most recent call last):
+#   File "<stdin>", line 1, in <module>
+# KeyError: 'secret'
+
+ +

Counter thì khác

+
print(c['py'])
+# 3
+print(c.get('recret'))
+# None
+print(c['secret'])
+# 0
+
+ +

Truy cập 1 key không tồn tại trong Counter trả về 0, method __missing__ quyết định điều này:

+
object.__missing__(self, key)
+
+    Called by dict.__getitem__() to implement self[key] for dict subclasses
+    when key is not in the dictionary.
+
+ +

Code Counter https://github.com/python/cpython/blob/v3.11.0/Lib/collections/__init__.py#L599C1-L602C17:

+
def __missing__(self, key):
+    'The count of elements not in the Counter is zero.'
+    # Needed so that self[missing_item] does not raise KeyError
+    return 0
+
+ +

Giờ thì cách hoạt động của Counter đã khác hoàn toàn dict, vậy counter có phải dict?

+

Kết luận

+

Nói đúng cũng phải, nói không đúng cũng phải, chủ yếu là người nghe muốn nghe cái gì.

+

Hết.

+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/cv2.html b/cv2.html new file mode 100644 index 0000000..b1a3f12 --- /dev/null +++ b/cv2.html @@ -0,0 +1,131 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Xin chào opencv-python +

+
+

+ by Pymier0 +

+
+
+
+

img

+

OpenCV (https://opencv.org/)

+
+

OpenCV (Open Source Computer Vision Library: http://opencv.org) is an open-source library that includes several hundreds of computer vision algorithms.

+
+

là một thư viện xử lý hình ảnh (ảnh, video, …) viết bằng C++ nhưng có thể dùng từ nhiều ngôn ngữ khác nhau, trong Python, cài

+
pip install opencv-python
+
+ +

sau đó để dùng import cv2 as cv.

+

Mở file ảnh

+
img = cv.imread("/home/hvn/Pictures/python-logo.png")
+print(type(img))
+# Output: <class 'numpy.ndarray'>
+
+ +

Vậy một file ảnh trong OpenCV chính là 1 ndarray của numpy.

+

Ghi file

+
cv.imwrite("filename.png", img)
+
+ +

Tham khảo

+
    +
  • https://docs.opencv.org/4.x/d6/d00/tutorial_py_root.html
  • +
+

Hết.

+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/dataclass-leak.html b/dataclass-leak.html new file mode 100644 index 0000000..63faeec --- /dev/null +++ b/dataclass-leak.html @@ -0,0 +1,239 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Lộ mật khẩu cùng dataclass +

+
+

+ by Pymier0 +

+
+
+
+

dataclasses module là 1 standard library rất mới, xuất hiện ở Python 3.7, với tác dụng:

+
+

This module provides a decorator and functions for automatically adding generated special methods such as __init__() and __repr__() to user-defined classes.

+
+

https://peps.python.org/pep-0557/

+

đây là một tính năng được yêu thích ở các bản Python mới (3.7+).

+

dataclass giúp viết class ngắn hơn, không phải tự gõ __init__

+

dataclass là một decorator, dùng trên đầu class:

+
from dataclasses import dataclass
+
+@dataclass
+class User:
+    name: str
+    age: int
+    email: str
+
+u = User("Pymier", 8, "hvn@pymi.vn")
+print(u)
+# User(name='Pymier', age=8, email='hvn@pymi.vn')
+
+ +

Tiện vậy có gì mà không tốt?

+

dataclass luôn in ra mọi attribute

+

Mặc định, khi print một object tạo bởi class dùng dataclass, nó sẽ in ra mọi attribute. +Giả sử một ngày, cần chứa mật khẩu trong class User, đoạn code trở thành:

+
@dataclass
+class User:
+    name: str
+    age: int
+    email: str
+    password: str
+
+u = User("Pymier", 8, "hvn@pymi.vn", "hunter42")
+print(u)
+# User(name='Pymier', age=8, email='hvn@pymi.vn', password='hunter42')
+
+ +

Vậy là nhờ dùng dataclass, ta VÔ TÌNH để lộ mật khẩu, in ra màn hình, render ra website hay theo log file lộ ra ngoài…

+

Sửa lại thế nào?

+

muốn print object ra gì, rõ là phải sửa __str__!, thêm method __str__ vào

+
    ...
+    def __str__(self) -> str:
+        return f"{self.name=} {self.age=} {self.email=}"
+
+u = User("Pymier", 8, "hvn@pymi.vn", "hunter42")
+print(u)
+# self.name='Pymier' self.age=8 self.email='hvn@pymi.vn'
+
+ +

Kết quả giờ không còn hiện ra password nữa, ta lặng lẽ ỉm đi cái security bug này và không ai biết tới, thật tuyệt vời!

+

Xong chưa? nên dừng lại 1 chút suy nghĩ rồi hãy đọc tiếp.

+

Đoạn code ban đầu hoàn hảo, được thiết kế chủ ý dùng dataclass vì có thể in ra mọi thông tin, thì sau 1 thay đổi “nhỏ” và thường là do 1 người khác thay đổi, trở thành code có lỗ hổng bảo mật (security vulnerability). +Người ta thường so sánh ngành lập trình với ngành xây dựng, software thì toàn bug còn nhà thì mấy khi “hỏng”? software bị thay đổi hàng ngày, còn nhà thì vài năm hay thập kỷ mới thay.

+

Tạo 1 class mới chứa dataclass này

+

Tạo 1 team class dùng dataclass, và in ra:

+
@dataclass
+class Team:
+    members: list[User]
+
+t = Team(members=[u])
+print(t)
+# Team(members=[User(name='Pymier', age=8, email='hvn@pymi.vn', password='hunter42')])
+
+ +

dù ta đã viết __str__ cho User, nhưng khi in ra Team object, nó sử dụng __repr__ chứ không dùng __str__, như dòng đầu tiên trong tài liệu của dataclasses đã viết:

+
+

automatically adding generated special methods such as __init__() and __repr__() to user-defined classes

+
+

Cách fix: thêm __repr__:

+
+

If a class defines __repr__() but not __str__(), then __repr__() is also used when an “informal” string representation of instances of that class is required.

+
+
from dataclasses import dataclass
+
+@dataclass
+class User:
+    name: str
+    age: int
+    email: str
+    password: str
+    def __repr__(self) -> str:
+        return f"{self.name=} {self.age=} {self.email=}"
+
+u = User("Pymier", 8, "hvn@pymi.vn", "hunter42")
+print(u)
+# self.name='Pymier' self.age=8 self.email='hvn@pymi.vn'
+@dataclass
+class Team:
+    members: list[User]
+
+t = Team(members=[u])
+print(t)
+# Team(members=[self.name='Pymier' self.age=8 self.email='hvn@pymi.vn'])
+
+ +

Bạn đọc tham khảo sự khác biệt giữa __str____repr__: https://stackoverflow.com/questions/1436703/what-is-the-difference-between-str-and-repr

+

Bất ngờ thay, mấy ông già không “cool” không “trend” không dùng dataclass không gặp phải vấn đề này:

+
from dataclasses import dataclass
+
+class User:
+    def __init__(self, name: str, age: int, email: str, password: str):
+        self.name = name
+        self.age = age
+        self.email = email
+        self.password = password
+
+u = User("Pymier", 8, "hvn@pymi.vn", "hunter42")
+print(u, repr(u))
+# <__main__.User object at 0x7f9efd510410> <__main__.User object at 0x7f9efd510410>
+
+ +

Update: dataclasses cũng đã tính đến chuyện này, để KHÔNG hiện một attribute khi __repr__, dùng field(repr=False)

+
from dataclasses import dataclass, field
+
+@dataclass
+class User:
+    name: str
+    age: int
+    email: str
+    password: str = field(repr=False)
+u = User("Pymier", 8, "hvn@pymi.vn", "hunter42")
+print(u)
+# User(name='Pymier', age=8, email='hvn@pymi.vn')
+
+ +

Tham khảo

+

https://docs.python.org/3.11/library/dataclasses.html

+

Xem list các method được dataclass autogenerate tại https://peps.python.org/pep-0557/#abstract

+

Kết luận

+

dataclasses tiện lợi, giúp viết ít code hơn, nhưng cần biết những gì nó “tự động” để tránh bất ngờ. +Default không có nghĩa là không biết. Explicit is better than implicit.

+

Hết.

+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/dh_requests.html b/dh_requests.html new file mode 100644 index 0000000..e75921c --- /dev/null +++ b/dh_requests.html @@ -0,0 +1,148 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ [Deep Reading] Chọc sâu vào thư viện dùng phổ biến nhất của Python: lib requests +

+
+

+ by Pymier0 +

+
+
+
+

img

+

Nếu 10 năm trước, khi nói bạn “học sâu”, người ta nghĩ tới việc tìm hiểu thật kỹ, chọc sâu vào qua nhiều tầng lớp để nắm thật chắc kiến thức, thì sau 10 năm marketing, Deep Learning - một nhánh của AI, được dịch sang tiếng Việt với tên “học sâu”, đã chiếm mất nghĩa của từ học sâu ban đầu.

+

Vậy nên chúng tôi gọi là deep reading, nghe cho nó đỡ lẫn.

+

Loạt bài viết deep reading đi sâu vào các dòng code của các thư viện Python, xem chúng được viết thế nào, bí hiểm ra sao. Và lên thớt đầu tiên chính là thư viện được download nhiều số 1 của Python: requests.

+

Cấu trúc thư mục

+

Code thư viện nằm gọn trong 1 thư mục tên “requests”, cùng level nó có 1 thư mục tên “tests”. Trong requests, chỉ có 18 files py và không có thư mục con nào:

+
ls requests/
+adapters.py  auth.py   compat.py   exceptions.py  hooks.py     _internal_utils.py  packages.py  status_codes.py  utils.py
+api.py       certs.py  cookies.py  help.py        __init__.py  models.py           sessions.py  structures.py    __version__.py
+
+ +

Dependencies

+

requests phụ thuộc trực tiếp vào 4 thư viện, đáng kể nhất là urllib3.

+
requires = [
+    "charset_normalizer>=2,<3",
+    "idna>=2.5,<4",
+    "urllib3>=1.21.1,<1.27",
+    "certifi>=2017.4.17",
+]
+
+ +

__init__.py

+

File đầu tiên nên đọc là __init__.py nó chứa những gì ta có thể truy cập khi gõ requests., ví dụ như requests.get. File này chỉ import các function, class từ các file khác vào.

+

api.py

+

Các function tiện lợi requests.get requests.post … đều nằm trong api.py. File này chứa các function ứng với các HTTP verb/method, và chỉ có vậy. Code cũng rất đơn giản:

+
def request(method, url, **kwargs):
+    with sessions.Session() as session:
+        return session.request(method=method, url=url, **kwargs)
+
+
+def get(url, params=None, **kwargs):
+    """... đã bỏ comment cho ngắn"""
+    return request("get", url, params=params, **kwargs)
+
+ +

get thực chất chỉ gọi request, function request tạo một Session và dùng nó để kết nối HTTP qua session.request.

+

sessions.py - chứa class Session, trái tim của requests

+

Một Session sẽ chứa đủ thông tin cần thiết như url, headers, auth, proxy, … rồi thực hiện gọi tới HTTP adapter để thực hiện kết nối.

+

adapters.py - nơi kết nối thực sự xảy ra

+

Dù là thực hiện kết nối HTTP thì trên Python cũng có rất nhiều thư viện, từ thư viện có sẵn của Python stdlib cho tới thư viện bên ngoài và dùng mặc định cho requests: urllib3. Function send trong adapters thực hiện kết nối với “Request” được chuẩn bị, và trả về Response. Code của send dài 150 dòng, không có gì quá phức tạp.

+

Còn gì không? còn nhiều và bạn có thể tự khám phá, nhưng từng ấy cũng đủ vừa sâu để hiểu rõ hơn về requests, hơn hàng ngàn lập trình viên ngoài kia rồi.

+

Happy deeeeeep reading.

+

Hết.

+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/dict.get.html b/dict.get.html new file mode 100644 index 0000000..3ba713d --- /dev/null +++ b/dict.get.html @@ -0,0 +1,135 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ dict.get hay or? +

+
+

+ by Pymier0 +

+
+
+
+

dict.get(k, default) có bằng dict.get(k) or default?

+

or thường được dùng để trả về giá trị mặc định:

+
host = os.environ.get("host") or "localhost"
+
+ +

Nhưng:

+

or có cách hoạt động khá lắt léo:

+

các giá trị được biến thành boolean trước khi xử lý, với giá trị trong dict là +0, “”, [],… nó lại bị or coi là không có gì, và lấy vế phải ra dùng.

+

Nên nếu dict là :

+
d = {"red": 0, "yellow": 1, "green": 2}
+
+ +

thì kết quả khác nhau:

+
d.get("red", 3) == 0
+
+ +

còn

+
d.get("red") or 3 == 3
+
+ +

Kết luận

+
get(key, default=None, /) method of builtins.dict instance
+    Return the value for key if key is in the dictionary, else default.
+
+ +

Đôi khi nhìn qua thì giống, nhưng phải sờ tận tay mới biết khác chỗ nào.

+

Hết.

+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/dictkey.html b/dictkey.html new file mode 100644 index 0000000..3b14347 --- /dev/null +++ b/dictkey.html @@ -0,0 +1,160 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ k in dict.keys() chậm hơn k in dict bao nhiêu? +

+
+

+ by Pymier0 +

+
+
+
+

img

+

Python2, khi gọi dict.keys() sẽ trả về 1 list các key của dict. Một cải tiến +lớn của Python3 là không trả về list mà trả về những thứ tương tự +generator để tiết kiệm bộ nhớ.

+
In [32]: type({}.keys())
+Out[32]: dict_keys
+
+In [33]: type({}.items())
+Out[33]: dict_items
+
+In [34]: type({}.values())
+Out[34]: dict_values
+
+ +

Khi nhìn k in dict, đó là phép toán có độ phức tạp O(1), nhanh tức thì, thì +có thể đoán k in dict.keys() sẽ tìm k trong 1 generator iterator (hay nôm na +là 1 list), sẽ rất chậm. Dùng timeit để thử:

+
# import timeit
+
+In [38]: timeit.timeit('-1 in d', setup='d = {i:i for i in range(30_000_000)}', number=1)
+Out[38]: 2.053999196505174e-06
+
+In [39]: timeit.timeit('-1 in d.keys()', setup='d = {i:i for i in range(30_000_000)}', number=1)
+Out[39]: 5.510002665687352e-06
+
+ +

Thấy k in d.keys() chậm bằng nửa k in d, đúng là chậm hơn, nhưng chỉ 1 nửa. +Trong khi nếu là list, thì tìm kiếm phải chậm hơn dict hàng nghìn +lần.

+

Sự chênh lệch này hóa ra do gọi d.keys()

+
In [40]: timeit.timeit('d.keys()', setup='d = {i:i for i in range(30_000_000)}', number=1)
+Out[40]: 2.93600169243291e-06
+
+ +

Nếu trừ phần này, thì 2 đoạn code nhanh như nhau. Tại sao?

+

Lý do bởi Python3, dict.keys() trả về key view:

+
+

The objects returned by dict.keys(), dict.values() and dict.items() are view objects. They provide a dynamic view on the dictionary’s entries, which means that when the dictionary changes, the view reflects these changes.

+
+

Với chú ý keys view hoạt động như kiểu set chứ không phải list. Mà dict key hoạt động như set nên tốc độ là như nhau:

+
+

Keys views are set-like since their entries are unique and hashable. If all values are hashable, so that (key, value) pairs are unique and hashable, then the items view is also set-like. (Values views are not treated as set-like since the entries are generally not unique.)

+
+

Keys view dùng như set:

+
In [26]: d = {i:i for i in range(5)}
+
+In [27]: d2 = {i:i for i in range(4,10)}
+
+In [28]: d.keys() & d2.keys()
+Out[28]: {4}
+
+ +

Dù vậy, viết k in dict vẫn là nhanh, ngắn nhất.

+

Xem: https://docs.python.org/3/library/stdtypes.html#dictionary-view-objects

+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/dictorder.html b/dictorder.html new file mode 100644 index 0000000..fc6b99e --- /dev/null +++ b/dictorder.html @@ -0,0 +1,144 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Từ Python3.7 trở đi, các key trong dict có thứ tự +

+
+

+ by Pymier0 +

+
+
+
+

img

+

Các key sẽ theo thứ tự chúng được thêm vào dict.

+
In [1]: for i in {1:None, 3: None, 20: None, 2: None}:
+   ...:     print(i)
+   ...:
+1
+3
+20
+2
+
+ +

khác với trước kia, các key không có thứ tự (khác với ngẫu nhiên - ngẫu nhiên +là mỗi lần chạy qua key dict ra 1 kết quả khác nhau, còn ở đây là luôn +giống nhau trong 1 lần chạy code).

+
$ python2
+Python 2.7.18 (default, Mar  8 2021, 13:02:45)
+[GCC 9.3.0] on linux2
+Type "help", "copyright", "credits" or "license" for more information.
+>>> for i in {1:None, 3:None, 20:None, 2:None}:
+...     print(i)
+...
+1
+2
+3
+20
+>>> for i in {1:None, 3:None, 20:None, 2:None}:
+...     print(i)
+...
+1
+2
+3
+20
+
+ +

Tham khảo

+
    +
  • https://docs.python.org/3/whatsnew/3.7.html
  • +
  • https://mail.python.org/pipermail/python-dev/2017-December/151283.html
  • +
+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/dictvslist.html b/dictvslist.html new file mode 100644 index 0000000..6a86c70 --- /dev/null +++ b/dictvslist.html @@ -0,0 +1,137 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Tìm kiếm trong dict nhanh gấp list hàng ngàn lần +

+
+

+ by Pymier0 +

+
+
+
+

Python rất dễ dùng, để tìm kiếm chỉ cần gõ: i in X +dù X là list, dict, set, str, tuple đều chạy.

+

Code giống nhau, nhưng cách chạy thì khác nhau. Viết i in List so với +i in Dict hoặc i in Set có tốc độ khác nhau rất nhiều.

+

img

+

i in List: list là 1 array/list, để tìm 1 giá trị, Python sẽ kiểm tra lần +lượt từng giá trị từ đầu tới cuối cho đến khi tìm thấy. Tức trường hợp xấu nhất, +khi không thấy, Python phải kiểm tra hết len(List) phần tử. Số lần kiểm tra +tỷ lệ thuận với số phần tử, gọi là có độ phức tạp thuật toán về thời gian O(n).

+

Dùng timeit để đo:

+
$ python3 -m timeit --setup 'n=10_000; L=list(range(n))' 'n in L'
+5000 loops, best of 5: 55.2 usec per loop
+$ python3 -m timeit --setup 'n=100_000; L=list(range(n))' 'n in L'
+500 loops, best of 5: 569 usec per loop
+
+ +

Thấy khi list có 100_000 phần tử, trường hợp xấu nhất sẽ chậm hơn 10 lần so với +list có 10_000 phần tử.

+

Kiểu dict được tối ưu cho việc tìm kiếm (nên đặt tên là dictionary), việc tìm +kiếm diễn ra “tức thì”, không quan tâm dict lớn đến đâu. Việc tìm kiếm với tốc +độ cố định này gọi là thuật toán có độ phức tạp hằng số (constant), hay O(1):

+
$ python3 -m timeit --setup 'n = 10_000; D = {i: i**2 for i in range(n)}' 'n in D'
+20000000 loops, best of 5: 17.7 nsec per loop
+$ python3 -m timeit --setup 'n = 100_000; D = {i: i**2 for i in range(n)}' 'n in D'
+20000000 loops, best of 5: 18.3 nsec per loop
+
+ +

Trường hợp đầu tiên với 10_000, tìm trong dict nhanh hơn trong list 3000 lần, +trường hợp thứ 2 là 30_000 lần.

+

Khi cần tìm kiếm, nhớ dùng dict (và set).

+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/dis1.html b/dis1.html new file mode 100644 index 0000000..45d960c --- /dev/null +++ b/dis1.html @@ -0,0 +1,229 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Hello dis +

+
+

+ by Pymier0 +

+
+
+
+

Tiếp loạt bài về Python compiler.

+

img

+

Trong CPython compiler, ta đã biết CPython có thực +hiện việc compile code, sinh ra các bytecode, rồi bytecode sẽ được CPython VM +chạy. Khi import module, CPython sinh ra các file .pyc sau khi compile. Nội dung +các file này ở dạng binary, người dùng sẽ không thể đọc được.

+

Python có sẵn thư viện dis, giúp thực hiện “disassembly”, biến code thành +dạng mã bytecode đọc được. +Bài này giới thiệu các phép toán đơn giản để làm quen: +Tạo các file code .py đơn giản rồi chạy chúng với lệnh: python3 -m dis tenfile.py

+
# ==> d1.py <==
+x = 5
+
+# ==> d2.py <==
+x = 5
+y = 7
+
+# ==> d3.py <==
+x = 5
+y = 7
+z = x + y
+
+# ==> d4.py <==
+x = 5
+y = 7
+z = x + y
+s = x - y
+print(z)
+print(s)
+print(len("Python"))
+
+# ==> d5.py <==
+x = 5
+y = -x
+
+ +

File đầu tiên gán x = 5, số 1 trong cột đầu tiên thể hiện +dòng code.

+
$ python3 -m dis d1.py
+  1           0 LOAD_CONST               0 (5)
+              2 STORE_NAME               0 (x)
+              4 LOAD_CONST               1 (None)
+              6 RETURN_VALUE
+
+ +

5 là một giá trị có sẵn, Python thực hiện việc lấy nó LOAD_CONST, sau đó gán x = 5 với STORE_NAME. 2 dòng sau tạm không bàn tới và xem tiếp các ví dụ còn lại:

+

chạy d2.py sẽ không khác với d1.py, chỉ thêm phần LOAD_CONST cho 7 và thưc hiện STORE_NAME y = 7.

+

d3.py thêm một phép tính cộng, có BYTECODEBINARY_ADD.

+
d3.py
+  1           0 LOAD_CONST               0 (5)
+              2 STORE_NAME               0 (x)
+
+  2           4 LOAD_CONST               1 (7)
+              6 STORE_NAME               1 (y)
+
+  3           8 LOAD_NAME                0 (x)
+             10 LOAD_NAME                1 (y)
+             12 BINARY_ADD
+             14 STORE_NAME               2 (z)
+             16 LOAD_CONST               2 (None)
+             18 RETURN_VALUE
+
+ +

d4.py có phép trừ, có BYTECODEBINARY_SUBTRACT. +Chú ý chữ BINARY nói đây là phép toán có 2 toán tử như x + y hay x - y, một +loại phép toán khác chỉ có 1 toán tử như phép lấy số âm (-x), thì - là +UNARY_NEGATIVE.

+
$ python3 -m dis d5.py
+  1           0 LOAD_CONST               0 (5)
+              2 STORE_NAME               0 (x)
+
+  2           4 LOAD_NAME                0 (x)
+              6 UNARY_NEGATIVE
+              8 STORE_NAME               1 (y)
+             10 LOAD_CONST               1 (None)
+             12 RETURN_VALUE
+# d5.py
+x = 5
+y = -x
+
+ +

d4.py có gọi các function có sẵn, việc đầu tiên là lấy ra function với +LOAD_NAME, rồi gọi với CALL_FUNCTION. Chú ý sau mỗi lần gọi function có +POP_TOP, sẽ tìm hiểu ở bài sau.

+
d4.py
+  1           0 LOAD_CONST               0 (5)
+              2 STORE_NAME               0 (x)
+
+  2           4 LOAD_CONST               1 (7)
+              6 STORE_NAME               1 (y)
+
+  3           8 LOAD_NAME                0 (x)
+             10 LOAD_NAME                1 (y)
+             12 BINARY_ADD
+             14 STORE_NAME               2 (z)
+
+  4          16 LOAD_NAME                0 (x)
+             18 LOAD_NAME                1 (y)
+             20 BINARY_SUBTRACT
+             22 STORE_NAME               3 (s)
+
+  5          24 LOAD_NAME                4 (print)
+             26 LOAD_NAME                2 (z)
+             28 CALL_FUNCTION            1
+             30 POP_TOP
+
+  6          32 LOAD_NAME                4 (print)
+             34 LOAD_NAME                3 (s)
+             36 CALL_FUNCTION            1
+             38 POP_TOP
+
+  7          40 LOAD_NAME                4 (print)
+             42 LOAD_NAME                5 (len)
+             44 LOAD_CONST               2 ('Python')
+             46 CALL_FUNCTION            1
+             48 CALL_FUNCTION            1
+             50 POP_TOP
+             52 LOAD_CONST               3 (None)
+             54 RETURN_VALUE
+
+ +

BYTECODE trông lạ nhưng không hề khó.

+

https://docs.python.org/3/library/dis.html +Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/disfor.html b/disfor.html new file mode 100644 index 0000000..287c008 --- /dev/null +++ b/disfor.html @@ -0,0 +1,195 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Dịch ngược mã máy Python: for/while +

+
+

+ by Pymier0 +

+
+
+
+

Tiếp loạt bài về CPython compiler

+

img

+

Xem ví dụ tính tổng 1 tới 100 sau:

+

for

+
# for.py
+t = 0
+for i in range(1, 101):
+    t = t + i
+
+print(t)
+
+ +

Chạy dis

+
$ python3 -m dis for.py
+  1           0 LOAD_CONST               0 (0)
+              2 STORE_NAME               0 (t)
+
+  2           4 LOAD_NAME                1 (range)
+              6 LOAD_CONST               1 (1)
+              8 LOAD_CONST               2 (101)
+             10 CALL_FUNCTION            2
+             12 GET_ITER
+        >>   14 FOR_ITER                12 (to 28)
+             16 STORE_NAME               2 (i)
+
+  3          18 LOAD_NAME                0 (t)
+             20 LOAD_NAME                2 (i)
+             22 BINARY_ADD
+             24 STORE_NAME               0 (t)
+             26 JUMP_ABSOLUTE           14
+
+  5     >>   28 LOAD_NAME                3 (print)
+             30 LOAD_NAME                0 (t)
+             32 CALL_FUNCTION            1
+             34 POP_TOP
+             36 LOAD_CONST               3 (None)
+             38 RETURN_VALUE
+
+ +

sau khi LOAD xong function range và 2 tham số 1, 101, CALL_FUNCTION gọi function range.

+

Tại offset 12 có BYTECODE GET_ITER để lấy iterator từ object range.

+

offset 14 có đánh dấu vị trí jump >>, bắt đầu thực hiện +FOR_ITER, với đầu vào là iterator lấy ở offset 12, khi thực hiện xong việc lặp sẽ nhảy tới offset 28 (ngoài vòng for).

+

Code trong thân vòng lặp for kết thúc bằng BYTECODE JUMP_ABSOLUTE và nhảy tới offset 14, tức chạy xong thân vòng lặp thì chuyển tới vòng lặp tiếp theo.

+

while

+
# while.py
+t = 0
+i = 1
+while i < 101:
+    t = t + i
+
+print(t)
+
+ +

Chạy dis

+
$ python3 -m dis while.py
+  1           0 LOAD_CONST               0 (0)
+              2 STORE_NAME               0 (t)
+
+  2           4 LOAD_CONST               1 (1)
+              6 STORE_NAME               1 (i)
+
+  3     >>    8 LOAD_NAME                1 (i)
+             10 LOAD_CONST               2 (101)
+             12 COMPARE_OP               0 (<)
+             14 POP_JUMP_IF_FALSE       26
+
+  4          16 LOAD_NAME                0 (t)
+             18 LOAD_NAME                1 (i)
+             20 BINARY_ADD
+             22 STORE_NAME               0 (t)
+             24 JUMP_ABSOLUTE            8
+
+  6     >>   26 LOAD_NAME                2 (print)
+             28 LOAD_NAME                0 (t)
+             30 CALL_FUNCTION            1
+             32 POP_TOP
+             34 LOAD_CONST               3 (None)
+             36 RETURN_VALUE
+
+ +

Điều kiện theo sau vòng lặp while sẽ khiến while nhảy tới ngoài vòng lặp khi điều kiện sai (như if nhảy sang else), +dùng cùng BYTECODE POP_JUMP_IF_FALSE tới offset 26 - vị trí ngoài vòng lặp.

+

Hết thân vòng lặp while, JUMP_ABSOLUTE lại nhảy tới vòng lặp tiếp theo.

+

Kết luận

+

for và while đều sinh ra BYTECODE tương tự nhau. +Trong khi for có xuất hiện 2 BYTECODE mới là FOR_ITER và GET_ITER thì while chỉ toàn là JUMP.

+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/disfun.html b/disfun.html new file mode 100644 index 0000000..f8a6422 --- /dev/null +++ b/disfun.html @@ -0,0 +1,219 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Dịch ngược mã máy Python: function/lambda +

+
+

+ by Pymier0 +

+
+
+
+

Tiếp loạt bài về CPython compiler +img

+

Code định nghĩa 2 function dùng def và lambda:

+
# $ cat -n fun.py
+     1  def sum_two(x, y):
+     2      z = x + y
+     3      return z
+     4
+     5  a = 5
+     6  b = 7
+     7  sum_two(a, b)
+     8
+     9  double = lambda x: x * 2
+    10  double(5)
+
+ +

Chạy dis. Việc tạo function dùng def hay lambda đều sử dụng +BYTECODE MAKE_FUNCTION sau đó STORE_NAME

+
  1           0 LOAD_CONST               0 (<code object sum_two at 0x7fc64dcca190, file "fun.py", line 1>)
+              2 LOAD_CONST               1 ('sum_two')
+              4 MAKE_FUNCTION            0
+              6 STORE_NAME               0 (sum_two)
+
+  5           8 LOAD_CONST               2 (5)
+             10 STORE_NAME               1 (a)
+
+  6          12 LOAD_CONST               3 (7)
+             14 STORE_NAME               2 (b)
+
+  7          16 LOAD_NAME                0 (sum_two)
+             18 LOAD_NAME                1 (a)
+             20 LOAD_NAME                2 (b)
+             22 CALL_FUNCTION            2
+             24 POP_TOP
+
+  9          26 LOAD_CONST               4 (<code object <lambda> at 0x7fc64dcca2f0, file "fun.py", line 9>)
+             28 LOAD_CONST               5 ('<lambda>')
+             30 MAKE_FUNCTION            0
+             32 STORE_NAME               3 (double)
+
+ 10          34 LOAD_NAME                3 (double)
+             36 LOAD_CONST               2 (5)
+             38 CALL_FUNCTION            1
+             40 POP_TOP
+             42 LOAD_CONST               6 (None)
+             44 RETURN_VALUE
+
+Disassembly of <code object sum_two at 0x7fc64dcca190, file "fun.py", line 1>:
+  2           0 LOAD_FAST                0 (x)
+              2 LOAD_FAST                1 (y)
+              4 BINARY_ADD
+              6 STORE_FAST               2 (z)
+
+  3           8 LOAD_FAST                2 (z)
+             10 RETURN_VALUE
+
+Disassembly of <code object <lambda> at 0x7fc64dcca2f0, file "fun.py", line 9>:
+  9           0 LOAD_FAST                0 (x)
+              2 LOAD_CONST               1 (2)
+              4 BINARY_MULTIPLY
+              6 RETURN_VALUE
+
+ +

khác với các khái niệm trước, khi viết function sẽ thấy python dis riêng ra từng mục cho từng function.

+

Với function sum_two tại dòng 1, có riêng mục

+

Disassembly of <code object sum_two at 0x7fc64dcca190, file "fun.py", line 1>:

+

chú ý thêm sự khác biệt khi dùng biến trong function sử dụng LOAD_FAST thay vì LOAD_NAME, tạo biến sử dụng STORE_FAST thay STORE_NAME. function kết thúc bằng việc RETURN_VALUE.

+

Thế nhưng.. return cái gì? câu RETURN_VALUE không thấy ghi thêm gì sau, làm +sao biết nó return gì? +RETURN_VALUE sẽ return giá trị cuối cùng tính toán được. Như trong sum_two +sẽ return giá trị của z sau khi LOAD_FAST. Với lambda function, return +kết quả của phép nhân BINARY_MULTIPLY. Cụ thể hơn, trong Python VM gọi đây là +TOP OF STACK (TOS), kết quả của giá trị tính toán xong sẽ luôn nằm ở đây. +Vậy nếu muốn return giá trị không phải là tính cuối cùng thì sao?

+

Thử nghiệm với 2 function không trả về giá trị vừa tính xong:

+
     1  def r_none(x, y):
+     2      z = x + y
+     3      return
+     4
+     5
+     6  def r_i(x, y):
+     7      i = 8
+     8      z = x + y
+     9      return i
+
+ +

Kết quả dis

+
Disassembly of <code object r_none at 0x7fdd4b47bdf0, file "fun.py", line 1>:
+  2           0 LOAD_FAST                0 (x)
+              2 LOAD_FAST                1 (y)
+              4 BINARY_ADD
+              6 STORE_FAST               2 (z)
+
+  3           8 LOAD_CONST               0 (None)
+             10 RETURN_VALUE
+
+Disassembly of <code object r_i at 0x7fdd4b47f030, file "fun.py", line 6>:
+  7           0 LOAD_CONST               1 (8)
+              2 STORE_FAST               2 (i)
+
+  8           4 LOAD_FAST                0 (x)
+              6 LOAD_FAST                1 (y)
+              8 BINARY_ADD
+             10 STORE_FAST               3 (z)
+
+  9          12 LOAD_FAST                2 (i)
+             14 RETURN_VALUE
+
+ +

function r_none load giá trị None để return vì code chỉ ghi return không gì cả. +function r_i sẽ LOAD_FAST giá trị i rồi return i.

+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/disif.html b/disif.html new file mode 100644 index 0000000..cf3ee9c --- /dev/null +++ b/disif.html @@ -0,0 +1,156 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Dịch ngược mã máy Python: if/else +

+
+

+ by Pymier0 +

+
+
+
+

img

+

Tiếp loạt bài về CPython compiler

+
# ifelse.py
+x = 20
+if x > 18:
+    print("18+")
+else:
+    print("vaccine is coming")
+
+ +
 $ python3 -m dis i
+  1           0 LOAD_CONST               0 (20)
+              2 STORE_NAME               0 (x)
+
+  2           4 LOAD_NAME                0 (x)
+              6 LOAD_CONST               1 (18)
+              8 COMPARE_OP               4 (>)
+             10 POP_JUMP_IF_FALSE       22
+
+  3          12 LOAD_NAME                1 (print)
+             14 LOAD_CONST               2 ('18+')
+             16 CALL_FUNCTION            1
+             18 POP_TOP
+             20 JUMP_FORWARD             8 (to 30)
+
+  5     >>   22 LOAD_NAME                1 (print)
+             24 LOAD_CONST               3 ('vaccine is coming')
+             26 CALL_FUNCTION            1
+             28 POP_TOP
+        >>   30 LOAD_CONST               4 (None)
+             32 RETURN_VALUE
+
+ +

Code LOAD_CONST số 20, gán x = 20, +sau đó LOAD_NAME x và LOAD_CONST 18 để thực hiện so sánh. +So sánh sử dụng bytecode COMPARE_OP, biểu thức so sánh này nằm trong phần điều kiện của lệnh if. Xuất hiện BYTECODE mới:

+

POP_JUMP_IF_FALSE chú ý số 22 theo sau nó. +Để ý trước mỗi BYTECODE là một con số. +Số này là offset hiểu đơn giản là vị trí hay địa chỉ trong đoạn code.

+
    start index of operation within bytecode sequence
+
+ +

POP_JUMP_IF_FALSE 22 nói rằng sẽ JUMP (nhảy) tới +offset 22 nếu sai. Offset 22 ở đây chính là code trong khối else.

+

Sau khi hết code trong khối if, xuất hiện BYTECODE: +20 JUMP_FORWARD 8 (to 30) +tức JUMP tới offset 30. Rõ ràng khi code trong if chạy hết thì Python sẽ bỏ qua phần code else và nhảy tới vị trí ngoài if/else.

+

Trong output của dis, các vị trí JUMP (jump target) được ký hiệu bởi dấu >>. Xem trong code của dis

+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/disimport.html b/disimport.html new file mode 100644 index 0000000..376b6f3 --- /dev/null +++ b/disimport.html @@ -0,0 +1,187 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Dịch ngược mã máy Python: import +

+
+

+ by Pymier0 +

+
+
+
+

Tiếp loạt bài về CPython compiler

+

img

+
 rand.py 
+     1  import random
+     2  do = random.choice(['an', 'ngu'])
+     3  print(do)
+     4
+     5  from datetime import timedelta
+     6  print("Mot ngay co: {}s".format(timedelta(days=1).total_seconds()))
+     7
+     8  from math import *
+     9  print(factorial(5))
+
+ +

3 kiểu import trong Python. +Chạy dis:

+
#  python3 -m dis rand.py
+  1           0 LOAD_CONST               0 (0)
+              2 LOAD_CONST               1 (None)
+              4 IMPORT_NAME              0 (random)
+              6 STORE_NAME               0 (random)
+
+  2           8 LOAD_NAME                0 (random)
+             10 LOAD_METHOD              1 (choice)
+             12 LOAD_CONST               2 ('an')
+             14 LOAD_CONST               3 ('ngu')
+             16 BUILD_LIST               2
+             18 CALL_METHOD              1
+             20 STORE_NAME               2 (do)
+
+  3          22 LOAD_NAME                3 (print)
+             24 LOAD_NAME                2 (do)
+             26 CALL_FUNCTION            1
+             28 POP_TOP
+
+  5          30 LOAD_CONST               0 (0)
+             32 LOAD_CONST               4 (('timedelta',))
+             34 IMPORT_NAME              4 (datetime)
+             36 IMPORT_FROM              5 (timedelta)
+             38 STORE_NAME               5 (timedelta)
+             40 POP_TOP
+
+  6          42 LOAD_NAME                3 (print)
+             44 LOAD_CONST               5 ('Mot ngay co: {}s')
+             46 LOAD_METHOD              6 (format)
+             48 LOAD_NAME                5 (timedelta)
+             50 LOAD_CONST               6 (1)
+             52 LOAD_CONST               7 (('days',))
+             54 CALL_FUNCTION_KW         1
+             56 LOAD_METHOD              7 (total_seconds)
+             58 CALL_METHOD              0
+             60 CALL_METHOD              1
+             62 CALL_FUNCTION            1
+             64 POP_TOP
+
+  8          66 LOAD_CONST               0 (0)
+             68 LOAD_CONST               8 (('*',))
+             70 IMPORT_NAME              8 (math)
+             72 IMPORT_STAR
+
+  9          74 LOAD_NAME                3 (print)
+             76 LOAD_NAME                9 (factorial)
+             78 LOAD_CONST               9 (5)
+             80 CALL_FUNCTION            1
+             82 CALL_FUNCTION            1
+             84 POP_TOP
+             86 LOAD_CONST               1 (None)
+             88 RETURN_VALUE
+
+ +

import random sẽ dùng BYTECODE IMPORT_NAME, random sẽ trở thành 1 name +trong module này, được chứa STORE_NAME tương tự khi đặt x = 5. +Để gọi random.choice, đầu tiên phải +LOAD_NAME random rồi LOAD_METHOD choice.

+

Code from datetime import timedelta, chạy IMPORT_NAME datetime, nhưng không +STORE_NAME này mà IMPORT_FROM timedelta rồi STORE_NAME timedelta.

+

Code from math import * sử dụng BYTECODE IMPORT_STAR.

+

Chú ý khi gọi random.choice thì BYTECODECALL_METHOD do choice gắn liền +vào random - xem như 1 method của 1 module object, nhưng khi gọi +factorial đã from math import * thì BYTECODECALL_FUNCTION.

+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/django-template.html b/django-template.html new file mode 100644 index 0000000..c311bf6 --- /dev/null +++ b/django-template.html @@ -0,0 +1,205 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Django template gọi method không cần () +

+
+

+ by Pymier0 +

+
+
+
+

Python có 2 template engine phổ biến nhất: Jinja2 (hay dùng với Flask, SaltStack, Ansible…) và Django template.

+

Cả 2 khá giống nhau về cú pháp: viết for/if như Python, dùng function qua các filter có sẵn, đoạn code sau chạy +cho cả 2:

+
{% for fw in frameworks %}
+  {% if fw|length > 5 %}
+    <b>{{ fw }}</b>
+  {% else %}
+    {{ fw }}
+  {% endif %}
+{% endfor %}
+
+ +

nhưng sự khác biệt bắt đầu xuất hiện khi sử dụng method.

+

Gọi method trong Django template

+

Jinja2 sử dụng cú pháp gọi method như Python, kết quả không có gì ngạc nhiên khi gọi c.items():

+
import jinja2
+from collections import Counter
+
+c = Counter("meo meo go".split())
+print(jinja2.Template('{{ c.items }}').render(c=c))
+# <built-in method items of Counter object at 0x7fbe37ce64b0>
+print(jinja2.Template('{% for k, v in c.items() %} {{ k }} {{ v }} {%endfor%}').render(c=c))
+#  meo 2  go 1
+
+ +

Django template không dùng cú pháp gọi method

+
{{ counter.items() }}
+
+ +

Gặp error:

+
Could not parse the remainder: '()' from 'counter.items()'
+
+ +

Trong tài liệu Django template topic viết:

+
+

Dictionary lookup, attribute lookup and list-index lookups are implemented with a dot notation: + {{ my_dict.key }} + {{ my_object.attribute }} + {{ my_list.0 }} + If a variable resolves to a callable, the template system will call it with no arguments and use its result instead of the callable.

+
+

Khi sử dụng object.key, Django sẽ thử tìm object["key"], object.key hay nếu key là method, sẽ gọi object.key, +vì vậy sẽ phải viết {{ counter.items }}.

+

Chỉ đơn giản là không phải viết (), gì mà phức tạp?

+

Tìm bug

+

Cho 1 đoạn Django template, nhận context là 1 Counter:

+

Code views.py

+
from django.shortcuts import render
+from collections import Counter
+def index(request):
+    counter = Counter("red green green red red green green".split())
+    for k, v in counter.items():
+        print("from counter", k, v)
+    return render(request, "index.html", {"d": d, "counter": counter})
+
+ +

File templates/index.html

+
{% for k, v in counter.items %}
+  {{ k }} {{ v }}<br>
+{% endfor %}
+
+ +

Xảy ra exception tại dòng for:

+
TypeError at /
+
+'int' object is not iterable
+
+ +

có nghĩa counter.items trả về một int chứ không phải tuple (key, value) như dict.items vẫn trả về. +Khó hiểu hơn nữa, khi phần code tương tự trong function index vẫn chạy bình thường, tại sao counter.items trong Django template trả về int?

+

May mắn thay, tìm items() trong tài liệu reference của Django Template viết:

+

https://docs.djangoproject.com/en/4.2/ref/templates/language/#variables

+
+

Technically, when the template system encounters a dot, it tries the following lookups, in this order: + Dictionary lookup + Attribute or method lookup + Numeric index lookup +If the resulting value is callable, it is called with no arguments. The result of the call becomes the template value.

+
+

Cũng giống phần đã viết trong topic, nhưng nhấn mạnh hơn về thứ tự thử:

+
    +
  • tìm kiếm trong dict trước: object[key]
  • +
  • truy cập attribute, method cũng là 1 loại attribute: object.key
  • +
+

Trở lại ví dụ, counter.items đầu tiên sẽ gọi counter[items], +mà Counter trả về 0 với item không tồn tại thay vì raise KeyError, nên kết quả là 0, +không có lần gọi nào tới method counter.items().

+
+

This lookup order can cause some unexpected behavior with objects that override dictionary lookup. For example, consider the following code snippet that attempts to loop over a collections.defaultdict:

+

{% for k, v in defaultdict.items %} + Do something with k and v here…

+

Because dictionary lookup happens first, that behavior kicks in and provides a default value instead of using the intended .items() method. In this case, consider converting to a dictionary first

+
+

Giải pháp là biến Counter thành dict dict(c) trước khi đưa cho template.

+

Thành dict là xong??? cho đến 1 ngày đen đủi, khi dict của bạn chứa 1 key là items

+

Xem code tại https://github.com/django/django/blob/4.2/django/template/base.py#L867-L942

+

Kết luận

+

Magic không tự xảy ra, muốn dùng magic nhớ đọc doc, hãy thử, đừng đoán.

+

Hết.

+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/ellipsis.html b/ellipsis.html new file mode 100644 index 0000000..338de70 --- /dev/null +++ b/ellipsis.html @@ -0,0 +1,137 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Tôi muốn … em +

+
+

+ by Pymier0 +

+
+
+
+

... là một keyword hợp lệ trong Python, đã có từ rất lâu, và rất ít được biết tới.

+

img

+

... trong tiếng Anh là Ellipsis, trong Python có thể viết 1 trong 2 giá trị này, chúng có kiểu ellipsis chữ e viết thường.

+
>>> x = ...
+>>> type(x)
+<class 'ellipsis'>
+>>> Ellipsis is ...
+True
+
+ +

Ellipsis dùng để giữ chỗ khi cần, ví dụ khi viết function:

+
def pikachu():
+    ...
+
+ +

có tác dụng như

+
def pikachu()
+    pass
+
+ +

Mới đây, ... được trọng dụng trong 1 framework siêu hot mới nổi có tên FastAPI:

+
def update_item(
+    item_id: int, item: Item, user: User, importance: int = Body(...)
+)
+
+ +

Ellipsis

+
+

The same as the ellipsis literal “…”. Special value used mostly in conjunction with extended slicing syntax for user-defined container data types.

+
+

Tham khảo

+ +

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/enum.html b/enum.html new file mode 100644 index 0000000..ed26857 --- /dev/null +++ b/enum.html @@ -0,0 +1,184 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Enum trong Python +

+
+

+ by Pymier0 +

+
+
+
+

Khái niệm Enum có trong hầu hết các ngôn ngữ static typing nhưng mãi đến 3.4 +mới xuất hiện trong Python standard lib enum. +Tức Python developers đã nghĩ ra cách để không phải dùng +enum cũng chẳng sao… nhưng có thì tốt.

+

img

+

Enum để làm gì?

+

Enum là viết tắt của enumeration (tránh nhầm với function enumerate). +Enum là một bộ hữu hạn các tên ứng với các giá trị cố định. +Ví dụ +- kích thước áo chỉ có trong 1 tập giới hạn các giá trị XXS XS S M L XL XXL +- điểm số tây chỉ nằm trong tập A B C D E F +- các ngày trong tuần chỉ có từ thứ 2 đến chủ nhật +- các lá bài chỉ có 4 chất dô cơ tép bích :))))) (Diamond Heart Club Spade)

+

Làm thế nào để biểu diễn 4 chất này lên Python?

+

Cách 1: dùng string ‘D’, ‘H’, ‘C’, ‘S’

+

Nhược điểm: không so sánh được nếu ta có luật Spade < Club < Diamond < Heart, +lập trình viên cũng có thể gõ nhầm chữ P và để lại lỗi chỉ phát hiện ra lúc chạy.

+

Cách 2: đặt tên theo kiểu CONSTANT gán giá trị số - cách này phổ biến ở các +ngôn ngữ không có Enum.

+
SPADE = 1
+CLUB = 2
+DIAMOND = 3
+HEART = 4
+ALL_SUITS = (SPADE, CLUB, DIAMOND, HEART)
+
+ +

Nhược điểm: người dùng có thể đưa nhầm số khác vào và vẫn chạy, do bản chất +đây là các số. Không lấy được tập giá trị, hoặc phải tự tạo 1 list.

+

Và Enum đến giải cứu, để tạo 1 enum, kế thừa nó từ class Enum, ví dụ game 1 cây:

+
import random
+from typing import List, Tuple
+from enum import IntEnum, auto
+
+
+class Suit(IntEnum):
+    SPADE = auto()
+    CLUB = auto()
+    DIAMOND = auto()
+    HEART = auto()
+
+
+for s in Suit:
+    print(s, s.name, s.value)
+
+assert Suit.SPADE < Suit.HEART
+
+
+def create_cards() -> List[Tuple[int, Suit]]:
+    return [(i, s) for i in range(1, 13) for s in Suit]
+
+
+def play_1_cay(n: int, s: Suit):
+    r = random.choice(create_cards())
+    print("LeMe: {} vs No: {}: {}".format((n, s), r, (n, s) >= r))
+
+
+play_1_cay(8, Suit.CLUB)
+play_1_cay(8, 5)
+
+ +

Link glot.io

+

Output:

+
Suit.SPADE SPADE 1
+Suit.CLUB CLUB 2
+Suit.DIAMOND DIAMOND 3
+Suit.HEART HEART 4
+LeMe: (8, <Suit.CLUB: 2>) vs No: (5, <Suit.DIAMOND: 3>): True
+LeMe: (8, 5) vs No: (12, <Suit.HEART: 4>): False
+
+ +

mypy sẽ báo: ở dòng gọi play_1_cay(8, 5)

+
enumtest.py:24: error: Argument 2 to "play_1_cay" has incompatible type "int"; expected "Suit"
+
+ +

auto() tự sinh số tăng dần.

+

Happy enum.

+

Tham khảo

+
    +
  • https://docs.python.org/3/library/enum.html
  • +
+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/equal.html b/equal.html new file mode 100644 index 0000000..e76c42d --- /dev/null +++ b/equal.html @@ -0,0 +1,154 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Là và bằng +

+
+

+ by Pymier0 +

+
+
+
+

Nếu học Python tại Pymi.vn, chắc chắn bạn biết 1 bí mật ít khi dùng tới: 1 == True và 0 == False:

+
>>> True == 1 and False == 0
+True
+
+ +

Thậm chí có thể đem tính toán:

+
>>> True + True - False
+2
+
+ +

bí mật này dẫn tới một số vấn đề “kỳ lạ sau”.

+
>>> xs = [False, True, 2, 1, 0]
+>>> xs.index(0)
+0
+
+ +

sẽ có ít nhất 68% được hỏi trả lời sai câu này, dù trình độ code Python lâu đến mấy. +Lý do vì trong thực tế, gần như không bao giờ gặp trường hợp như vậy, việc tạo 1 list chứa cả boolean lẫn integer đã là code quá dở rồi.

+

Ngày hôm qua, trên “room Telegram chat sách” của Pymi (tham gia Slack vào #chem-gio để ra nhập), thành viên cốt cán @no7kai có đố:

+
dic = {True: 'yes', 1: 'no', 1.0: 'maybe'}
+dic[True]
+dic[1]
+
+ +

no7kai> Nhìn code đọc luôn kq ae.

+

Ta cheating, mang luôn đi chạy

+
>>> dic = {True: 'yes', 1: 'no', 1.0: 'maybe'}
+>>> dic
+{True: 'maybe'}
+
+ +

có người sẽ thấy ngạc nhiên, nhưng Python có ghi rõ trong doc (tất nhiên cái phần doc này cũng không mấy ai đọc:

+
+

A dictionary’s keys are almost arbitrary values. Values that are not hashable, that is, values containing lists, dictionaries or other mutable types (that are compared by value rather than by object identity) may not be used as keys. Numeric types used for keys obey the normal rules for numeric comparison: if two numbers compare equal (such as 1 and 1.0) then they can be used interchangeably to index the same dictionary entry. (Note however, that since computers store floating-point numbers as approximations it is usually unwise to use them as dictionary keys.)

+
+

mapping-types-dict

+

Khi giá trị bằng nhau, chúng được dùng thay thế nhau

+
>>> dic
+{True: 'maybe'}
+>>> dic[1]
+'maybe'
+
+ +

Ví dụ ban đầu và ví dụ này, Python đều so sánh == để thực hiện các phép tính.

+

Hỏi khó

+

vậy còn set? set([True, 1]) thì trả về mấy phần tử?

+

img

+
>>> set([True, 1, 1.0])
+{True}
+
+ +

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/feeds/all.atom.xml b/feeds/all.atom.xml new file mode 100644 index 0000000..20a064e --- /dev/null +++ b/feeds/all.atom.xml @@ -0,0 +1,5560 @@ + +Tin tức Python PyMI.vnhttps://n.pymi.vn/2024-10-15T00:00:00+07:00Biến nhỏ hơn thành lớn hơn với ast2024-10-15T00:00:00+07:002024-10-15T00:00:00+07:00Pymier0tag:n.pymi.vn,2024-10-15:/ast_intro.html<h3><span class="caps">AST</span> là&nbsp;gì</h3> +<p>Code Python ngoài dạng dòng text lập trình viên, có thể được biểu diễn ở dạng cây (tree), tên đầy đủ là Abstract Syntax Tree (<span class="caps">AST</span>).</p> +<p><span class="caps">AST</span> giúp việc duyệt qua từng biểu thức (expression), thành phần nhỏ nhất (token) trong mỗi biểu thức trở nên dễ …</p><h3><span class="caps">AST</span> là&nbsp;gì</h3> +<p>Code Python ngoài dạng dòng text lập trình viên, có thể được biểu diễn ở dạng cây (tree), tên đầy đủ là Abstract Syntax Tree (<span class="caps">AST</span>).</p> +<p><span class="caps">AST</span> giúp việc duyệt qua từng biểu thức (expression), thành phần nhỏ nhất (token) trong mỗi biểu thức trở nên dễ dàng hơn nhiều so với dạng text, vì vậy các tool check code ưa chuộng việc dùng <span class="caps">AST</span> thay&nbsp;text.</p> +<h3>Biến code text thành&nbsp;tree</h3> +<p>Sử dụng <code>ast.parse</code> để biến code thành tree. Dùng <code>ast.dump</code> để hiển thị cây ở dạng&nbsp;text:</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">ast</span> +<span class="o">&gt;&gt;&gt;</span> <span class="nb">print</span><span class="p">(</span><span class="n">ast</span><span class="o">.</span><span class="n">dump</span><span class="p">(</span><span class="n">ast</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="s1">&#39;1+1&lt;2&#39;</span><span class="p">),</span> <span class="n">indent</span><span class="o">=</span><span class="mi">4</span><span class="p">))</span> +<span class="n">Module</span><span class="p">(</span> + <span class="n">body</span><span class="o">=</span><span class="p">[</span> + <span class="n">Expr</span><span class="p">(</span> + <span class="n">value</span><span class="o">=</span><span class="n">Compare</span><span class="p">(</span> + <span class="n">left</span><span class="o">=</span><span class="n">BinOp</span><span class="p">(</span> + <span class="n">left</span><span class="o">=</span><span class="n">Constant</span><span class="p">(</span><span class="n">value</span><span class="o">=</span><span class="mi">1</span><span class="p">),</span> + <span class="n">op</span><span class="o">=</span><span class="n">Add</span><span class="p">(),</span> + <span class="n">right</span><span class="o">=</span><span class="n">Constant</span><span class="p">(</span><span class="n">value</span><span class="o">=</span><span class="mi">1</span><span class="p">)),</span> + <span class="n">ops</span><span class="o">=</span><span class="p">[</span> + <span class="n">Lt</span><span class="p">()],</span> + <span class="n">comparators</span><span class="o">=</span><span class="p">[</span> + <span class="n">Constant</span><span class="p">(</span><span class="n">value</span><span class="o">=</span><span class="mi">2</span><span class="p">)]))],</span> + <span class="n">type_ignores</span><span class="o">=</span><span class="p">[])</span> +</code></pre></div> + +<p><span class="caps">AST</span> không bị ảnh hưởng khi thêm space vào giữa các token, hay thậm chí xuống dòng,&nbsp;comment:</p> +<div class="highlight"><pre><span></span><code><span class="p">(</span><span class="mi">1</span><span class="o">+</span><span class="mi">1</span> + + <span class="o">&lt;</span> +<span class="c1">#&lt;</span> + <span class="mi">2</span><span class="p">)</span> +</code></pre></div> + +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">ast</span> +<span class="o">&gt;&gt;&gt;</span> <span class="nb">print</span><span class="p">(</span><span class="n">ast</span><span class="o">.</span><span class="n">dump</span><span class="p">(</span><span class="n">ast</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="s2">&quot;&quot;&quot;(1+1</span> +<span class="s2">...</span> +<span class="s2">... &lt;</span> +<span class="s2">... #&lt;</span> +<span class="s2">... 2)&quot;&quot;&quot;</span><span class="p">),</span> <span class="n">indent</span><span class="o">=</span><span class="mi">4</span><span class="p">))</span> +<span class="n">Module</span><span class="p">(</span> + <span class="n">body</span><span class="o">=</span><span class="p">[</span> + <span class="n">Expr</span><span class="p">(</span> + <span class="n">value</span><span class="o">=</span><span class="n">Compare</span><span class="p">(</span> + <span class="n">left</span><span class="o">=</span><span class="n">BinOp</span><span class="p">(</span> + <span class="n">left</span><span class="o">=</span><span class="n">Constant</span><span class="p">(</span><span class="n">value</span><span class="o">=</span><span class="mi">1</span><span class="p">),</span> + <span class="n">op</span><span class="o">=</span><span class="n">Add</span><span class="p">(),</span> + <span class="n">right</span><span class="o">=</span><span class="n">Constant</span><span class="p">(</span><span class="n">value</span><span class="o">=</span><span class="mi">1</span><span class="p">)),</span> + <span class="n">ops</span><span class="o">=</span><span class="p">[</span> + <span class="n">Lt</span><span class="p">()],</span> + <span class="n">comparators</span><span class="o">=</span><span class="p">[</span> + <span class="n">Constant</span><span class="p">(</span><span class="n">value</span><span class="o">=</span><span class="mi">2</span><span class="p">)]))],</span> + <span class="n">type_ignores</span><span class="o">=</span><span class="p">[])</span> +</code></pre></div> + +<p>kết quả vẫn giống với <code>1+1&lt;2</code>.</p> +<h3>Biến nhỏ hơn thành lớn&nbsp;hơn</h3> +<p>Đề bài: biến phép toán so sánh nhỏ hơn thành lớn&nbsp;hơn.</p> +<p>Nếu sử dụng biến đổi text, ta có thể&nbsp;viết:</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="n">s</span> <span class="o">=</span> <span class="s2">&quot;1+1&lt;2&quot;</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">s</span><span class="o">.</span><span class="n">replace</span><span class="p">(</span><span class="s2">&quot;&lt;&quot;</span><span class="p">,</span> <span class="s2">&quot;&gt;&quot;</span><span class="p">)</span> +<span class="s1">&#39;1+1&gt;2&#39;</span> +</code></pre></div> + +<p>đơn giản! và sai&nbsp;nếu:</p> +<ul> +<li>s là <code>1+1&lt;&lt;2</code> (binary&nbsp;shift)</li> +<li>s là 1 dòng&nbsp;comment</li> +<li>hay s là 1 string&nbsp;&#8216;1+1&lt;2&#8217;.</li> +</ul> +<p>Mọi giải pháp sử dụng biến đổi text (string method/regex) đều gặp phải đủ trường hợp phức tạp như 3 trường hợp trên. <span class="caps">AST</span> giải quyết vấn đề gọn gàng đơn giản hơn&nbsp;nhiều.</p> +<h3>Dùng <span class="caps">AST</span> biến nhỏ thành&nbsp;lớn</h3> +<p>Viết 4 trường hợp thành 5 dòng code. Trong 5 dòng này, ta chỉ muốn dòng 2 bị thay&nbsp;đổi.</p> +<div class="highlight"><pre><span></span><code>body = &quot;&quot;&quot;1+1&lt;&lt;2 +1+1&lt;2 +2+2==3 +&#39;1111&lt;2&#39; + #1+1&lt;2 +&quot;&quot;&quot; +</code></pre></div> + +<p>Dump <span class="caps">AST</span> ra màn&nbsp;hình:</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span><span class="w"> </span><span class="n">tree</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ast</span><span class="p">.</span><span class="n">parse</span><span class="p">(</span><span class="n">body</span><span class="p">)</span><span class="w"></span> +<span class="o">&gt;&gt;&gt;</span><span class="w"> </span><span class="n">print</span><span class="p">(</span><span class="n">ast</span><span class="p">.</span><span class="n">dump</span><span class="p">(</span><span class="n">tree</span><span class="p">,</span><span class="w"> </span><span class="n">indent</span><span class="o">=</span><span class="mi">4</span><span class="p">))</span><span class="w"></span> +<span class="n">Module</span><span class="p">(</span><span class="w"></span> +<span class="w"> </span><span class="n">body</span><span class="o">=[</span><span class="w"></span> +<span class="w"> </span><span class="n">Expr</span><span class="p">(</span><span class="w"></span> +<span class="w"> </span><span class="n">value</span><span class="o">=</span><span class="n">BinOp</span><span class="p">(</span><span class="w"></span> +<span class="w"> </span><span class="n">left</span><span class="o">=</span><span class="n">BinOp</span><span class="p">(</span><span class="w"></span> +<span class="w"> </span><span class="n">left</span><span class="o">=</span><span class="n">Constant</span><span class="p">(</span><span class="n">value</span><span class="o">=</span><span class="mi">1</span><span class="p">),</span><span class="w"></span> +<span class="w"> </span><span class="n">op</span><span class="o">=</span><span class="n">Add</span><span class="p">(),</span><span class="w"></span> +<span class="w"> </span><span class="n">right</span><span class="o">=</span><span class="n">Constant</span><span class="p">(</span><span class="n">value</span><span class="o">=</span><span class="mi">1</span><span class="p">)),</span><span class="w"></span> +<span class="w"> </span><span class="n">op</span><span class="o">=</span><span class="n">LShift</span><span class="p">(),</span><span class="w"></span> +<span class="w"> </span><span class="n">right</span><span class="o">=</span><span class="n">Constant</span><span class="p">(</span><span class="n">value</span><span class="o">=</span><span class="mi">2</span><span class="p">))),</span><span class="w"></span> +<span class="w"> </span><span class="n">Expr</span><span class="p">(</span><span class="w"></span> +<span class="w"> </span><span class="n">value</span><span class="o">=</span><span class="n">Compare</span><span class="p">(</span><span class="w"></span> +<span class="w"> </span><span class="n">left</span><span class="o">=</span><span class="n">BinOp</span><span class="p">(</span><span class="w"></span> +<span class="w"> </span><span class="n">left</span><span class="o">=</span><span class="n">Constant</span><span class="p">(</span><span class="n">value</span><span class="o">=</span><span class="mi">1</span><span class="p">),</span><span class="w"></span> +<span class="w"> </span><span class="n">op</span><span class="o">=</span><span class="n">Add</span><span class="p">(),</span><span class="w"></span> +<span class="w"> </span><span class="n">right</span><span class="o">=</span><span class="n">Constant</span><span class="p">(</span><span class="n">value</span><span class="o">=</span><span class="mi">1</span><span class="p">)),</span><span class="w"></span> +<span class="w"> </span><span class="n">ops</span><span class="o">=[</span><span class="w"></span> +<span class="w"> </span><span class="n">Lt</span><span class="p">()</span><span class="o">]</span><span class="p">,</span><span class="w"></span> +<span class="w"> </span><span class="n">comparators</span><span class="o">=[</span><span class="w"></span> +<span class="w"> </span><span class="n">Constant</span><span class="p">(</span><span class="n">value</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span><span class="o">]</span><span class="p">)),</span><span class="w"></span> +<span class="w"> </span><span class="n">Expr</span><span class="p">(</span><span class="w"></span> +<span class="w"> </span><span class="n">value</span><span class="o">=</span><span class="n">Compare</span><span class="p">(</span><span class="w"></span> +<span class="w"> </span><span class="n">left</span><span class="o">=</span><span class="n">BinOp</span><span class="p">(</span><span class="w"></span> +<span class="w"> </span><span class="n">left</span><span class="o">=</span><span class="n">Constant</span><span class="p">(</span><span class="n">value</span><span class="o">=</span><span class="mi">2</span><span class="p">),</span><span class="w"></span> +<span class="w"> </span><span class="n">op</span><span class="o">=</span><span class="n">Add</span><span class="p">(),</span><span class="w"></span> +<span class="w"> </span><span class="n">right</span><span class="o">=</span><span class="n">Constant</span><span class="p">(</span><span class="n">value</span><span class="o">=</span><span class="mi">2</span><span class="p">)),</span><span class="w"></span> +<span class="w"> </span><span class="n">ops</span><span class="o">=[</span><span class="w"></span> +<span class="w"> </span><span class="n">Eq</span><span class="p">()</span><span class="o">]</span><span class="p">,</span><span class="w"></span> +<span class="w"> </span><span class="n">comparators</span><span class="o">=[</span><span class="w"></span> +<span class="w"> </span><span class="n">Constant</span><span class="p">(</span><span class="n">value</span><span class="o">=</span><span class="mi">3</span><span class="p">)</span><span class="o">]</span><span class="p">)),</span><span class="w"></span> +<span class="w"> </span><span class="n">Expr</span><span class="p">(</span><span class="w"></span> +<span class="w"> </span><span class="n">value</span><span class="o">=</span><span class="n">Constant</span><span class="p">(</span><span class="n">value</span><span class="o">=</span><span class="c">&#39;1111&lt;2&#39;))],</span><span class="w"></span> +<span class="w"> </span><span class="n">type_ignores</span><span class="o">=[]</span><span class="p">)</span><span class="w"></span> +</code></pre></div> + +<p>Thấy chỉ có 4 biểu thức (Expression), dòng comment không có ý nghĩa khi chạy code nên đã được bỏ qua. +<span class="caps">AST</span> nhận biết được dòng đầu dùng <code>LShift</code> (binary left shift) và Expr cuối là 1 string constant. +Duyệt qua từng Expr trong tree.body cho tới khi thấy Compare dùng <code>ops[0]</code> kiểu <code>Lt</code> (less than - nhỏ hơn) thì biến thành Gt (greater than - lớn&nbsp;hơn).</p> +<div class="highlight"><pre><span></span><code><span class="k">for</span> <span class="n">expr</span> <span class="ow">in</span> <span class="n">tree</span><span class="o">.</span><span class="n">body</span><span class="p">:</span> + <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">expr</span><span class="o">.</span><span class="n">value</span><span class="p">,</span> <span class="n">ast</span><span class="o">.</span><span class="n">Compare</span><span class="p">)</span> <span class="ow">and</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">expr</span><span class="o">.</span><span class="n">value</span><span class="o">.</span><span class="n">ops</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">ast</span><span class="o">.</span><span class="n">Lt</span><span class="p">):</span> + <span class="n">expr</span><span class="o">.</span><span class="n">value</span><span class="o">.</span><span class="n">ops</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">ast</span><span class="o">.</span><span class="n">Gt</span><span class="p">()</span> +<span class="nb">print</span><span class="p">(</span><span class="n">ast</span><span class="o">.</span><span class="n">unparse</span><span class="p">(</span><span class="n">tree</span><span class="p">))</span> +</code></pre></div> + +<p>Kết&nbsp;quả</p> +<div class="highlight"><pre><span></span><code><span class="mi">1</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">2</span> +<span class="mi">1</span> <span class="o">+</span> <span class="mi">1</span> <span class="o">&gt;</span> <span class="mi">2</span> +<span class="mi">2</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">3</span> +<span class="s1">&#39;1111&lt;2&#39;</span> +</code></pre></div> + +<p>nhỏ đã thành to như mong&nbsp;ước.</p> +<h4>Biến đổi <span class="caps">AST</span> với&nbsp;Transformer</h4> +<p>Không phải GenAI Transformer deep learning architecture mà là NodeTransformer, class giúp biến đổi&nbsp;Node.</p> +<div class="highlight"><pre><span></span><code><span class="n">tree</span> <span class="o">=</span> <span class="n">ast</span><span class="o">.</span><span class="n">parse</span><span class="p">(</span><span class="n">body</span><span class="p">)</span> +<span class="k">class</span> <span class="nc">RewriteOp</span><span class="p">(</span><span class="n">ast</span><span class="o">.</span><span class="n">NodeTransformer</span><span class="p">):</span> + + <span class="k">def</span> <span class="nf">visit_Lt</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">node</span><span class="p">):</span> + <span class="k">return</span> <span class="n">ast</span><span class="o">.</span><span class="n">Gt</span><span class="p">()</span> + +<span class="n">node</span> <span class="o">=</span> <span class="n">RewriteOp</span><span class="p">()</span><span class="o">.</span><span class="n">visit</span><span class="p">(</span><span class="n">tree</span><span class="p">)</span> +<span class="nb">print</span><span class="p">(</span><span class="n">ast</span><span class="o">.</span><span class="n">unparse</span><span class="p">(</span><span class="n">node</span><span class="p">))</span> +</code></pre></div> + +<p>Kết&nbsp;quả</p> +<div class="highlight"><pre><span></span><code><span class="mf">1</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mf">1</span><span class="w"> </span><span class="o">&lt;&lt;</span><span class="w"> </span><span class="mf">2</span><span class="w"></span> +<span class="mf">1</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mf">1</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="mf">2</span><span class="w"></span> +<span class="mf">2</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mf">2</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mf">3</span><span class="w"></span> +<span class="err">&#39;</span><span class="mf">1111</span><span class="o">&lt;</span><span class="mf">2</span><span class="err">&#39;</span><span class="w"></span> +</code></pre></div> + +<p>khi <code>visit</code> duyệt tới node <code>Lt</code>, method <code>visit_Lt</code> được gọi, trả về một node mới thay cho node Lt cũ, ở đây thay bằng node&nbsp;Gt.</p> +<p>Dùng <code>unparse</code> để biến <span class="caps">AST</span> thành code. Code này có <span class="caps">AST</span> tương đương với code viết tay, nhưng không giống hệt, không có&nbsp;comment.</p> +<p>Chạy online tại đây <a href="https://glot.io/snippets/h0vnmlw2nm">https://glot.io/snippets/h0vnmlw2nm</a></p> +<h3>Kết&nbsp;luận</h3> +<p><span class="caps">AST</span> giúp check và biến đổi code dễ dàng khi dùng text là muôn vàn khó&nbsp;khăn.</p> +<p>Hết.</p> +<p><span class="caps">HVN</span> at <a href="http://pymi.vn">http://pymi.vn</a> and <a href="https://www.familug.org">https://www.familug.org</a>.</p> +<p><a href="https://www.familug.org/p/ung-ho.html">Ủng hộ tác giả&nbsp;🍺</a></p>Một số insights từ file sao kê của Mặt trận Tổ Quốc Việt Nam2024-09-15T00:00:00+07:002024-09-15T00:00:00+07:00tung491tag:n.pymi.vn,2024-09-15:/saoke.html<p>Nhân dịp ngày hội check var phông bạt toàn quốc thứ 6 ngày 13/9/2024, một pymier rảnh rỗi đã ngồi vọc file sao kê từ <span class="caps">MTTQ</span> <span class="caps">VN</span>. Dưới đây là một số insights từ <a href="https://docs.google.com/spreadsheets/d/1qEFjpatScpsf7znaVcrp3lFZpRtMJm5mMeMCVqN_38s/edit?usp=sharing">file sao kê đó</a> (đã convert từ <a href="https://drive.google.com/file/d/18dIWiReYtJkyuQ_8vSBJWweGaD71rBpu/view">pdf</a> sang&nbsp;csv)</p> +<h2>Tổng số&nbsp;tiền</h2> +<p><img alt="Tổng số tiền" src="images/total_saoke_amount.png"></p> +<p>Với …</p><p>Nhân dịp ngày hội check var phông bạt toàn quốc thứ 6 ngày 13/9/2024, một pymier rảnh rỗi đã ngồi vọc file sao kê từ <span class="caps">MTTQ</span> <span class="caps">VN</span>. Dưới đây là một số insights từ <a href="https://docs.google.com/spreadsheets/d/1qEFjpatScpsf7znaVcrp3lFZpRtMJm5mMeMCVqN_38s/edit?usp=sharing">file sao kê đó</a> (đã convert từ <a href="https://drive.google.com/file/d/18dIWiReYtJkyuQ_8vSBJWweGaD71rBpu/view">pdf</a> sang&nbsp;csv)</p> +<h2>Tổng số&nbsp;tiền</h2> +<p><img alt="Tổng số tiền" src="images/total_saoke_amount.png"></p> +<p>Với tổng 200,364 giao dịch trong 10 ngày từ 1-10/9/2024, tổng số tiền mà <span class="caps">MTTQ</span> <span class="caps">VN</span> nhận được là 135 tỷ đồng với trung bình mỗi giao dịch là 674,183 nghìn đồng và giao dịch có giá trị lớn nhất là 1 tỷ&nbsp;đồng.</p> +<h2>Số lượng giao dịch mỗi&nbsp;ngày</h2> +<p><img alt="Số lượng giao dịch mỗi ngày" src="images/distribution_daily.png"> +Bão Yagi đổ bộ vào đất liền Việt Nam vào ngày 7/9. Ta có thể thấy ngày hôm sau (8/9), số lượng giao dịch gửi vào <span class="caps">MTTQ</span> đã tăng đột biến gấp gần 10 lần với 503 giao dịch. Đáng kinh ngạc hơn nữa, ngày 9/9, số lượng giao dịch đã tăng lên hơn 6 ngàn giao dịch, gấp 12 lần ngày hôm trước. Và đến ngày 10/9, tổng số giao dịch trong ngày đã lên đến 191 nghìn giao&nbsp;dịch.</p> +<h2>Thống kê về một số patterns trong phần nội dung giao&nbsp;dịch</h2> +<h3>Của ít lòng&nbsp;nhiều</h3> +<p><img alt="Của ít lòng nhều" src="images/cuaitlongnhieu.png"> +Pattern &#8220;cua it long nhieu&#8221; xuất hiện trong 1715 giao dịch, với trung bình giá trị là 205 nghìn đồng, giá trị lớn nhất là 5 triệu&nbsp;đồng.</p> +<h3>Tập&nbsp;thể</h3> +<p><img alt="Tập thể" src="images/tapthe.png"> +Pattern gây sốt trong cộng đồng mạng là &#8220;tập thể&#8221; với 121 giao dịch, với trung bình giá trị là 9 triệu đồng, giá trị lớn nhất là 300 triệu đồng. Nhưng phần gây sốt là xuất hiện một số giao dịch của một số tổ chức tập thể với giá trị bé như 2 hay 10 nghìn&nbsp;đồng.</p> +<h3>Học sinh sinh&nbsp;viên</h3> +<p><img alt="Học sinh sinh viên" src="images/hssv.png"></p> +<p>Dù là học sinh sinh viên đã đóng góp 1580 giao dịch, với giá trị trung bình 273 nghìn đồng và cao nhất là 50 triệu&nbsp;đồng.</p> +<h3>Các doanh&nbsp;nghiệp</h3> +<p><img alt="Các doanh nghiệp" src="images/company_related.png"></p> +<p>Các doanh nghiệp đóng góp 396 giao dịch. Trung bình mỗi giao dịch là 12.7 triệu đồng và giao dịch có giá trị lớn nhất là 100 triệu&nbsp;đồng.</p> +<p>Jupyter Notebook được sử dụng có thể xem tại <a href="https://gist.github.com/tung491/fde52f83ca79fda886b24fb524cfae82">đây</a>.</p> +<p>Hết.</p>slice[::-1] là slice[len(slice):0:-1] hay slice[0:len(slice):-1]?2024-08-19T00:00:00+07:002024-08-19T00:00:00+07:00Pymier0tag:n.pymi.vn,2024-08-19:/slice_reverse.html<h3>Slice là&nbsp;gì</h3> +<p>Slice là cú pháp tiện dụng của Python để lấy 1 list con của list đã&nbsp;có.</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="n">names</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;c&#39;</span><span class="p">,</span> <span class="s1">&#39;java&#39;</span><span class="p">,</span> <span class="s1">&#39;js&#39;</span><span class="p">,</span> <span class="s1">&#39;php&#39;</span><span class="p">,</span> <span class="s1">&#39;python&#39;</span><span class="p">,</span> <span class="s1">&#39;go&#39;</span><span class="p">,</span> <span class="s1">&#39;rust&#39;</span><span class="p">,</span> <span class="s1">&#39;elixir&#39;</span><span class="p">]</span> +</code></pre></div> + +<p>Với cú pháp <code>slice[START:STOP:STEP]</code>, <span class="caps">START</span> và <span class="caps">STOP</span> là index bắt đầu và kết thúc, +<span class="caps">STEP</span> là bước dịch …</p><h3>Slice là&nbsp;gì</h3> +<p>Slice là cú pháp tiện dụng của Python để lấy 1 list con của list đã&nbsp;có.</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="n">names</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;c&#39;</span><span class="p">,</span> <span class="s1">&#39;java&#39;</span><span class="p">,</span> <span class="s1">&#39;js&#39;</span><span class="p">,</span> <span class="s1">&#39;php&#39;</span><span class="p">,</span> <span class="s1">&#39;python&#39;</span><span class="p">,</span> <span class="s1">&#39;go&#39;</span><span class="p">,</span> <span class="s1">&#39;rust&#39;</span><span class="p">,</span> <span class="s1">&#39;elixir&#39;</span><span class="p">]</span> +</code></pre></div> + +<p>Với cú pháp <code>slice[START:STOP:STEP]</code>, <span class="caps">START</span> và <span class="caps">STOP</span> là index bắt đầu và kết thúc, +<span class="caps">STEP</span> là bước dịch chuyển của index, để chọn index tiếp&nbsp;theo.</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="n">names</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">names</span><span class="p">):</span><span class="mi">2</span><span class="p">]</span> +<span class="p">[</span><span class="s1">&#39;c&#39;</span><span class="p">,</span> <span class="s1">&#39;js&#39;</span><span class="p">,</span> <span class="s1">&#39;python&#39;</span><span class="p">,</span> <span class="s1">&#39;rust&#39;</span><span class="p">]</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">names</span><span class="p">[::</span><span class="mi">2</span><span class="p">]</span> +<span class="p">[</span><span class="s1">&#39;c&#39;</span><span class="p">,</span> <span class="s1">&#39;js&#39;</span><span class="p">,</span> <span class="s1">&#39;python&#39;</span><span class="p">,</span> <span class="s1">&#39;rust&#39;</span><span class="p">]</span> +</code></pre></div> + +<p>Ở ví dụ trên lấy các phần tử có index 0, 0+2 = 2, 2+2 = 4, 4+2 =&nbsp;6.</p> +<ul> +<li><span class="caps">START</span> khi không ghi cụ thể giá trị trước dấu : có giá trị mặc định là&nbsp;0.</li> +<li><span class="caps">STOP</span> mặc định là <code>len(slice)</code>, tức&nbsp;8.</li> +<li><span class="caps">STEP</span> nếu không ghi, mặc định là <code>1</code>.</li> +</ul> +<p>Một tính năng thú vị của slice khiến nhiều người phỏng vấn tỏ ra khó chịu khi hỏi: hãy đảo ngược 1 list/str và câu trả lời là <code>names[::-1]</code>.</p> +<div class="highlight"><pre><span></span><code><span class="s s-Atom">&gt;&gt;&gt;</span> <span class="s s-Atom">names</span><span class="p">[</span><span class="o">::-</span><span class="mi">1</span><span class="p">]</span> +<span class="p">[</span><span class="s s-Atom">&#39;elixir&#39;</span><span class="p">,</span> <span class="s s-Atom">&#39;rust&#39;</span><span class="p">,</span> <span class="s s-Atom">&#39;go&#39;</span><span class="p">,</span> <span class="s s-Atom">&#39;python&#39;</span><span class="p">,</span> <span class="s s-Atom">&#39;php&#39;</span><span class="p">,</span> <span class="s s-Atom">&#39;js&#39;</span><span class="p">,</span> <span class="s s-Atom">&#39;java&#39;</span><span class="p">,</span> <span class="s s-Atom">&#39;c&#39;</span><span class="p">]</span> +</code></pre></div> + +<p>Khi sử dụng <span class="caps">STEP</span> = -1, cách tính sẽ thế nào? names[::-1] là phương án a hay&nbsp;b?</p> +<ul> +<li>a)&nbsp;names[len(names):0:-1]</li> +<li>b)&nbsp;names[0:len(names):-1]</li> +</ul> +<h3>Tìm lời giải slice với step -1 bằng builtin&nbsp;function</h3> +<p>Theo&nbsp;reference,</p> +<p><a href="https://docs.python.org/3/reference/datamodel.html#objects-values-and-types">https://docs.python.org/3/reference/datamodel.html#objects-values-and-types</a></p> +<p>Thử sử dụng built-in function <code>slice</code> để test 2 trường hợp&nbsp;trên:</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="n">S</span> <span class="o">=</span> <span class="nb">slice</span><span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="kc">None</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> +<span class="o">&gt;&gt;&gt;</span> <span class="nb">print</span><span class="p">(</span><span class="n">S</span><span class="o">.</span><span class="n">indices</span><span class="o">.</span><span class="vm">__doc__</span><span class="p">)</span> +<span class="n">S</span><span class="o">.</span><span class="n">indices</span><span class="p">(</span><span class="nb">len</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="p">(</span><span class="n">start</span><span class="p">,</span> <span class="n">stop</span><span class="p">,</span> <span class="n">stride</span><span class="p">)</span> + +<span class="n">Assuming</span> <span class="n">a</span> <span class="n">sequence</span> <span class="n">of</span> <span class="n">length</span> <span class="nb">len</span><span class="p">,</span> <span class="n">calculate</span> <span class="n">the</span> <span class="n">start</span> <span class="ow">and</span> <span class="n">stop</span> +<span class="n">indices</span><span class="p">,</span> <span class="ow">and</span> <span class="n">the</span> <span class="n">stride</span> <span class="n">length</span> <span class="n">of</span> <span class="n">the</span> <span class="n">extended</span> <span class="nb">slice</span> <span class="n">described</span> <span class="n">by</span> +<span class="n">S</span><span class="o">.</span> <span class="n">Out</span> <span class="n">of</span> <span class="n">bounds</span> <span class="n">indices</span> <span class="n">are</span> <span class="n">clipped</span> <span class="ow">in</span> <span class="n">a</span> <span class="n">manner</span> <span class="n">consistent</span> <span class="k">with</span> <span class="n">the</span> +<span class="n">handling</span> <span class="n">of</span> <span class="n">normal</span> <span class="n">slices</span><span class="o">.</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">names</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;c&#39;</span><span class="p">,</span> <span class="s1">&#39;java&#39;</span><span class="p">,</span> <span class="s1">&#39;js&#39;</span><span class="p">,</span> <span class="s1">&#39;php&#39;</span><span class="p">,</span> <span class="s1">&#39;python&#39;</span><span class="p">,</span> <span class="s1">&#39;go&#39;</span><span class="p">,</span> <span class="s1">&#39;rust&#39;</span><span class="p">,</span> <span class="s1">&#39;elixir&#39;</span><span class="p">]</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">start</span><span class="p">,</span> <span class="n">stop</span><span class="p">,</span> <span class="n">step</span> <span class="o">=</span> <span class="n">S</span><span class="o">.</span><span class="n">indices</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">names</span><span class="p">))</span> +<span class="o">&gt;&gt;&gt;</span> <span class="nb">print</span><span class="p">(</span><span class="n">start</span><span class="p">,</span> <span class="n">stop</span><span class="p">,</span> <span class="n">step</span><span class="p">)</span> +<span class="mi">7</span> <span class="o">-</span><span class="mi">1</span> <span class="o">-</span><span class="mi">1</span> +</code></pre></div> + +<p><strong>Câu trả lời là khi step = -1 thì start index là <code>len(names)-1</code>, stop index là <code>-1</code></strong>. Cả 2 phương án nêu từ đầu đều&nbsp;sai.</p> +<h3>Tìm lời giải slice với step -1 bằng cách đọc code&nbsp;CPython</h3> +<p>Tải code Python&nbsp;v3.12.0</p> +<div class="highlight"><pre><span></span><code>$ git clone https://github.com/python/cpython --depth <span class="m">1</span> --branch v3.12.0 python3120 +Cloning into <span class="s1">&#39;python3120&#39;</span>... +... +Resolving deltas: <span class="m">100</span>% <span class="o">(</span><span class="m">513</span>/513<span class="o">)</span>, <span class="k">done</span>. +Note: switching to <span class="s1">&#39;0fb18b02c8ad56299d6a2910be0bab8ad601ef24&#39;</span>. + +You are <span class="k">in</span> <span class="s1">&#39;detached HEAD&#39;</span> state. You can look around, make experimental +... +$ du -csh python3120 +136M python3120 +136M total +$ <span class="nb">cd</span> python3120 +</code></pre></div> + +<p>Tìm file code C chứa từ khóa <code>slice</code>:</p> +<div class="highlight"><pre><span></span><code>$ grep -Rin <span class="s1">&#39;slice&#39;</span> -l <span class="p">|</span> grep -F .c +Python/ast_opt.c +Python/import.c +Python/Python-ast.c +Python/compile.c +... +Objects/sliceobject.c +Objects/bytes_methods.c +Objects/listobject.c +... +</code></pre></div> + +<p>trong file <code>sliceobject.c</code>:</p> +<div class="highlight"><pre><span></span><code><span class="cm">/* Implementation of slice.indices. */</span><span class="w"></span> + +<span class="k">static</span><span class="w"> </span><span class="n">PyObject</span><span class="o">*</span><span class="w"></span> +<span class="nf">slice_indices</span><span class="p">(</span><span class="n">PySliceObject</span><span class="o">*</span><span class="w"> </span><span class="n">self</span><span class="p">,</span><span class="w"> </span><span class="n">PyObject</span><span class="o">*</span><span class="w"> </span><span class="n">len</span><span class="p">)</span><span class="w"></span> +<span class="p">{</span><span class="w"></span> +<span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">start</span><span class="p">,</span><span class="w"> </span><span class="o">*</span><span class="n">stop</span><span class="p">,</span><span class="w"> </span><span class="o">*</span><span class="n">step</span><span class="p">;</span><span class="w"></span> +<span class="w"> </span><span class="p">...</span><span class="w"></span> +<span class="w"> </span><span class="n">error</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">_PySlice_GetLongIndices</span><span class="p">(</span><span class="n">self</span><span class="p">,</span><span class="w"> </span><span class="n">length</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="n">start</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="n">stop</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="n">step</span><span class="p">);</span><span class="w"></span> +<span class="w"> </span><span class="p">...</span><span class="w"></span> +<span class="p">}</span><span class="w"></span> +<span class="p">...</span><span class="w"></span> + +<span class="kt">int</span><span class="w"></span> +<span class="n">_PySlice_GetLongIndices</span><span class="p">(</span><span class="n">PySliceObject</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="p">,</span><span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">*</span><span class="n">length</span><span class="p">,</span><span class="w"></span> +<span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">**</span><span class="n">start_ptr</span><span class="p">,</span><span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">**</span><span class="n">stop_ptr</span><span class="p">,</span><span class="w"></span> +<span class="w"> </span><span class="n">PyObject</span><span class="w"> </span><span class="o">**</span><span class="n">step_ptr</span><span class="p">)</span><span class="w"></span> +<span class="p">{</span><span class="w"></span> +<span class="w"> </span><span class="cm">/* Convert step to an integer; raise for zero step. */</span><span class="w"></span> +<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">self</span><span class="o">-&gt;</span><span class="n">step</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">Py_None</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> +<span class="w"> </span><span class="n">step</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">_PyLong_GetOne</span><span class="p">();</span><span class="w"></span> +<span class="w"> </span><span class="n">step_is_negative</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"></span> +<span class="w"> </span><span class="p">}</span><span class="w"></span> +<span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"></span> +<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">step_sign</span><span class="p">;</span><span class="w"></span> +<span class="w"> </span><span class="p">...</span><span class="w"></span> +<span class="w"> </span><span class="n">step_is_negative</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">step_sign</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"></span> +<span class="w"> </span><span class="p">}</span><span class="w"></span> + +<span class="w"> </span><span class="cm">/* Find lower and upper bounds for start and stop. */</span><span class="w"></span> +<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">step_is_negative</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> +<span class="w"> </span><span class="n">lower</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PyLong_FromLong</span><span class="p">(</span><span class="mi">-1L</span><span class="p">);</span><span class="w"></span> +<span class="w"> </span><span class="p">...</span><span class="w"></span> +<span class="w"> </span><span class="n">upper</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PyNumber_Add</span><span class="p">(</span><span class="n">length</span><span class="p">,</span><span class="w"> </span><span class="n">lower</span><span class="p">);</span><span class="w"></span> +<span class="w"> </span><span class="p">}</span><span class="w"></span> +<span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"></span> +<span class="w"> </span><span class="n">lower</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">_PyLong_GetZero</span><span class="p">();</span><span class="w"></span> +<span class="w"> </span><span class="n">upper</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Py_NewRef</span><span class="p">(</span><span class="n">length</span><span class="p">);</span><span class="w"></span> +<span class="w"> </span><span class="p">}</span><span class="w"></span> + +<span class="w"> </span><span class="cm">/* Compute start. */</span><span class="w"></span> +<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">self</span><span class="o">-&gt;</span><span class="n">start</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">Py_None</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> +<span class="w"> </span><span class="n">start</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Py_NewRef</span><span class="p">(</span><span class="n">step_is_negative</span><span class="w"> </span><span class="o">?</span><span class="w"> </span><span class="n">upper</span><span class="w"> </span><span class="o">:</span><span class="w"> </span><span class="n">lower</span><span class="p">);</span><span class="w"></span> +<span class="w"> </span><span class="p">}</span><span class="w"></span> +<span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"></span> +<span class="w"> </span><span class="p">...</span><span class="w"></span> +<span class="w"> </span><span class="p">}</span><span class="w"></span> + +<span class="w"> </span><span class="cm">/* Compute stop. */</span><span class="w"></span> +<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">self</span><span class="o">-&gt;</span><span class="n">stop</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">Py_None</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> +<span class="w"> </span><span class="n">stop</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Py_NewRef</span><span class="p">(</span><span class="n">step_is_negative</span><span class="w"> </span><span class="o">?</span><span class="w"> </span><span class="n">lower</span><span class="w"> </span><span class="o">:</span><span class="w"> </span><span class="n">upper</span><span class="p">);</span><span class="w"></span> +<span class="w"> </span><span class="p">}</span><span class="w"></span> +<span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"></span> +<span class="w"> </span><span class="n">stop</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">evaluate_slice_index</span><span class="p">(</span><span class="n">self</span><span class="o">-&gt;</span><span class="n">stop</span><span class="p">);</span><span class="w"></span> +<span class="w"> </span><span class="p">...</span><span class="w"></span> +<span class="w"> </span><span class="p">}</span><span class="w"></span> +<span class="w"> </span><span class="p">...</span><span class="w"></span> +<span class="p">}</span><span class="w"></span> +</code></pre></div> + +<p>đoạn code này thực hiện tính toán khi step &lt; 0, start = upper = length + lower và stop = lower =&nbsp;-1.</p> +<p>Code CPython khá phức tạp, <strong>việc in ra start, stop khi thực hiện slice[::-1] để lại như bài tập cho bạn&nbsp;đọc.</strong></p> +<p>Vậy code nào đơn giản hơn? Python có rất nhiều bản khác nhau và đơn giản nhất có thể kể tới&nbsp;MicroPython.</p> +<h3>MicroPython - khỏe như Python mà nhỏ cỡ&nbsp;micro</h3> +<div class="highlight"><pre><span></span><code>$ git clone https://github.com/micropython/micropython --depth <span class="m">1</span> --branch v1.23.0 +Cloning into <span class="s1">&#39;micropython&#39;</span>... +remote: Enumerating objects: <span class="m">5784</span>, <span class="k">done</span>. +remote: Counting objects: <span class="m">100</span>% <span class="o">(</span><span class="m">5784</span>/5784<span class="o">)</span>, <span class="k">done</span>. + +$ du -csh micropython +55M micropython +55M total +</code></pre></div> + +<p>Mở file <code>py/objslice.c</code> tìm function <code>slice_indices</code> và thêm dòng printf để in start, stop,&nbsp;step:</p> +<div class="highlight"><pre><span></span><code><span class="nb nb-Type">void</span><span class="w"> </span><span class="n">mp_obj_slice_indices</span><span class="p">(</span><span class="n">mp_obj_t</span><span class="w"> </span><span class="n">self_in</span><span class="p">,</span><span class="w"> </span><span class="n">mp_int_t</span><span class="w"> </span><span class="n">length</span><span class="p">,</span><span class="w"> </span><span class="n">mp_bound_slice_t</span><span class="w"> </span><span class="o">*</span><span class="n">result</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> +<span class="w"> </span><span class="n">mp_obj_slice_t</span><span class="w"> </span><span class="o">*</span><span class="bp">self</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">MP_OBJ_TO_PTR</span><span class="p">(</span><span class="n">self_in</span><span class="p">);</span><span class="w"></span> +<span class="w"> </span><span class="n">mp_int_t</span><span class="w"> </span><span class="n">start</span><span class="p">,</span><span class="w"> </span><span class="n">stop</span><span class="p">,</span><span class="w"> </span><span class="n">step</span><span class="p">;</span><span class="w"></span> +<span class="w"> </span><span class="o">...</span><span class="w"></span> +<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">step</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> +<span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Positive</span><span class="w"> </span><span class="n">step</span><span class="w"></span> +<span class="w"> </span><span class="o">...</span><span class="w"></span> +<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"></span> +<span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Negative</span><span class="w"> </span><span class="n">step</span><span class="w"></span> +<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="bp">self</span><span class="o">-&gt;</span><span class="n">start</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">mp_const_none</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> +<span class="w"> </span><span class="n">start</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">length</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"></span> +<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"></span> +<span class="w"> </span><span class="n">start</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">mp_obj_get_int</span><span class="p">(</span><span class="bp">self</span><span class="o">-&gt;</span><span class="n">start</span><span class="p">);</span><span class="w"></span> +<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">start</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> +<span class="w"> </span><span class="n">start</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="n">length</span><span class="p">;</span><span class="w"></span> +<span class="w"> </span><span class="p">}</span><span class="w"></span> +<span class="w"> </span><span class="n">start</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">MIN</span><span class="p">(</span><span class="n">length</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="n">MAX</span><span class="p">(</span><span class="n">start</span><span class="p">,</span><span class="w"> </span><span class="o">-</span><span class="mi">1</span><span class="p">));</span><span class="w"></span> +<span class="w"> </span><span class="p">}</span><span class="w"></span> + +<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="bp">self</span><span class="o">-&gt;</span><span class="n">stop</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">mp_const_none</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> +<span class="w"> </span><span class="n">stop</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">-</span><span class="mi">1</span><span class="p">;</span><span class="w"></span> +<span class="w"> </span><span class="p">}</span><span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="p">{</span><span class="w"></span> +<span class="w"> </span><span class="n">stop</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">mp_obj_get_int</span><span class="p">(</span><span class="bp">self</span><span class="o">-&gt;</span><span class="n">stop</span><span class="p">);</span><span class="w"></span> +<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">stop</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> +<span class="w"> </span><span class="n">stop</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="n">length</span><span class="p">;</span><span class="w"></span> +<span class="w"> </span><span class="p">}</span><span class="w"></span> +<span class="w"> </span><span class="n">stop</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">MIN</span><span class="p">(</span><span class="n">length</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="n">MAX</span><span class="p">(</span><span class="n">stop</span><span class="p">,</span><span class="w"> </span><span class="o">-</span><span class="mi">1</span><span class="p">));</span><span class="w"></span> +<span class="w"> </span><span class="p">}</span><span class="w"></span> +<span class="w"> </span><span class="p">}</span><span class="w"></span> + +<span class="w"> </span><span class="n">result</span><span class="o">-&gt;</span><span class="n">start</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">start</span><span class="p">;</span><span class="w"></span> +<span class="w"> </span><span class="n">result</span><span class="o">-&gt;</span><span class="n">stop</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">stop</span><span class="p">;</span><span class="w"></span> +<span class="w"> </span><span class="n">result</span><span class="o">-&gt;</span><span class="n">step</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">step</span><span class="p">;</span><span class="w"></span> +<span class="w"> </span><span class="o">//</span><span class="w"> </span><span class="n">Thêm</span><span class="w"> </span><span class="n">dòng</span><span class="w"> </span><span class="n">này</span><span class="p">,</span><span class="w"> </span><span class="n">và</span><span class="w"> </span><span class="n">thêm</span><span class="w"> </span><span class="c1">#include &lt;stdio.h&gt; vào đầu file.</span><span class="w"></span> +<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s2">&quot;DEBUG: start </span><span class="si">%ld</span><span class="s2"> stop </span><span class="si">%ld</span><span class="s2"> step </span><span class="si">%ld</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">,</span><span class="w"> </span><span class="n">start</span><span class="p">,</span><span class="w"> </span><span class="n">stop</span><span class="p">,</span><span class="w"> </span><span class="n">step</span><span class="p">);</span><span class="w"></span> +<span class="p">}</span><span class="w"></span> +</code></pre></div> + +<p>Build&nbsp;binary:</p> +<div class="highlight"><pre><span></span><code><span class="err">$</span> <span class="n">cd</span> <span class="n">micropython</span> +<span class="err">$</span> <span class="n">cd</span> <span class="n">ports</span><span class="o">/</span><span class="n">unix</span> +<span class="err">$</span> <span class="n">make</span> <span class="n">submodules</span> +<span class="err">$</span> <span class="n">sudo</span> <span class="n">apt</span> <span class="n">install</span> <span class="o">-</span><span class="n">y</span> <span class="n">libffi</span><span class="o">-</span><span class="n">dev</span> +<span class="err">$</span> <span class="n">make</span> <span class="o">-</span><span class="n">j</span> +<span class="o">...</span> +<span class="n">make</span> <span class="o">-</span><span class="n">j</span> <span class="mi">28</span><span class="p">,</span><span class="mi">59</span><span class="n">s</span> <span class="n">user</span> <span class="mi">4</span><span class="p">,</span><span class="mi">52</span><span class="n">s</span> <span class="n">system</span> <span class="mi">369</span><span class="o">%</span> <span class="n">cpu</span> <span class="mi">8</span><span class="p">,</span><span class="mi">970</span> <span class="n">total</span> +</code></pre></div> + +<p>Chỉ mất 8 giây để build trên máy dùng <code>AMD Ryzen 3 4300U</code>.</p> +<div class="highlight"><pre><span></span><code><span class="err">$</span> <span class="p">.</span><span class="o">/</span><span class="s s-Atom">build</span><span class="o">-</span><span class="s s-Atom">standard</span><span class="o">/</span><span class="s s-Atom">micropython</span> +<span class="nv">MicroPython</span> <span class="s s-Atom">v1</span><span class="mf">.23.0</span><span class="o">-</span><span class="s s-Atom">dirty</span> <span class="s s-Atom">on</span> <span class="mi">2024</span><span class="o">-</span><span class="mi">08</span><span class="o">-</span><span class="mi">19</span><span class="p">;</span> <span class="s s-Atom">linux</span> <span class="p">[</span><span class="nv">GCC</span> <span class="mf">11.4.0</span><span class="p">]</span> <span class="s s-Atom">version</span> +<span class="nv">Use</span> <span class="nv">Ctrl</span><span class="o">-</span><span class="nv">D</span> <span class="s s-Atom">to</span> <span class="s s-Atom">exit</span><span class="p">,</span> <span class="nv">Ctrl</span><span class="o">-</span><span class="nv">E</span> <span class="s s-Atom">for</span> <span class="s s-Atom">paste</span> <span class="s s-Atom">mode</span> +<span class="s s-Atom">&gt;&gt;&gt;</span> <span class="s s-Atom">names</span> <span class="o">=</span> <span class="p">[</span><span class="s s-Atom">&#39;c&#39;</span><span class="p">,</span> <span class="s s-Atom">&#39;java&#39;</span><span class="p">,</span> <span class="s s-Atom">&#39;js&#39;</span><span class="p">,</span> <span class="s s-Atom">&#39;php&#39;</span><span class="p">,</span> <span class="s s-Atom">&#39;python&#39;</span><span class="p">,</span> <span class="s s-Atom">&#39;go&#39;</span><span class="p">,</span> <span class="s s-Atom">&#39;rust&#39;</span><span class="p">,</span> <span class="s s-Atom">&#39;elixir&#39;</span><span class="p">]</span> +<span class="s s-Atom">&gt;&gt;&gt;</span> <span class="s s-Atom">names</span><span class="p">[</span><span class="o">::-</span><span class="mi">1</span><span class="p">]</span> +<span class="nv">DEBUG</span><span class="o">:</span> <span class="s s-Atom">start</span> <span class="mi">7</span> <span class="s s-Atom">stop</span> <span class="o">-</span><span class="mi">1</span> <span class="s s-Atom">step</span> <span class="o">-</span><span class="mi">1</span> +<span class="p">[</span><span class="s s-Atom">&#39;elixir&#39;</span><span class="p">,</span> <span class="s s-Atom">&#39;rust&#39;</span><span class="p">,</span> <span class="s s-Atom">&#39;go&#39;</span><span class="p">,</span> <span class="s s-Atom">&#39;python&#39;</span><span class="p">,</span> <span class="s s-Atom">&#39;php&#39;</span><span class="p">,</span> <span class="s s-Atom">&#39;js&#39;</span><span class="p">,</span> <span class="s s-Atom">&#39;java&#39;</span><span class="p">,</span> <span class="s s-Atom">&#39;c&#39;</span><span class="p">]</span> +</code></pre></div> + +<p>Vậy khi viết <code>slice[::-1]</code> start index là len(slice)-1, stop index là&nbsp;-1.</p> +<h3>Reverse thực hiện thế&nbsp;nào?</h3> +<p>Trong MicroPython <code>py/objlist.c</code> có&nbsp;code</p> +<div class="highlight"><pre><span></span><code><span class="k">static</span><span class="w"> </span><span class="n">mp_obj_t</span><span class="w"> </span><span class="nf">list_reverse</span><span class="p">(</span><span class="n">mp_obj_t</span><span class="w"> </span><span class="n">self_in</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> +<span class="w"> </span><span class="n">mp_check_self</span><span class="p">(</span><span class="n">mp_obj_is_type</span><span class="p">(</span><span class="n">self_in</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="n">mp_type_list</span><span class="p">));</span><span class="w"></span> +<span class="w"> </span><span class="n">mp_obj_list_t</span><span class="w"> </span><span class="o">*</span><span class="n">self</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">MP_OBJ_TO_PTR</span><span class="p">(</span><span class="n">self_in</span><span class="p">);</span><span class="w"></span> + +<span class="w"> </span><span class="n">mp_int_t</span><span class="w"> </span><span class="n">len</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">self</span><span class="o">-&gt;</span><span class="n">len</span><span class="p">;</span><span class="w"></span> +<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">mp_int_t</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">len</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="mi">2</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="o">++</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span> +<span class="w"> </span><span class="n">mp_obj_t</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">self</span><span class="o">-&gt;</span><span class="n">items</span><span class="p">[</span><span class="n">i</span><span class="p">];</span><span class="w"></span> +<span class="w"> </span><span class="n">self</span><span class="o">-&gt;</span><span class="n">items</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">self</span><span class="o">-&gt;</span><span class="n">items</span><span class="p">[</span><span class="n">len</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1</span><span class="p">];</span><span class="w"></span> +<span class="w"> </span><span class="n">self</span><span class="o">-&gt;</span><span class="n">items</span><span class="p">[</span><span class="n">len</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">a</span><span class="p">;</span><span class="w"></span> +<span class="w"> </span><span class="p">}</span><span class="w"></span> + +<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">mp_const_none</span><span class="p">;</span><span class="w"></span> +<span class="p">}</span><span class="w"></span> +</code></pre></div> + +<p>Hay code&nbsp;Python</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="n">names</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;c&#39;</span><span class="p">,</span> <span class="s1">&#39;java&#39;</span><span class="p">,</span> <span class="s1">&#39;js&#39;</span><span class="p">,</span> <span class="s1">&#39;php&#39;</span><span class="p">,</span> <span class="s1">&#39;python&#39;</span><span class="p">,</span> <span class="s1">&#39;go&#39;</span><span class="p">,</span> <span class="s1">&#39;rust&#39;</span><span class="p">,</span> <span class="s1">&#39;elixir&#39;</span><span class="p">]</span> +<span class="o">&gt;&gt;&gt;</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">length</span><span class="o">//</span><span class="mi">2</span><span class="p">):</span> +<span class="o">...</span> <span class="n">names</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">names</span><span class="p">[</span><span class="n">length</span><span class="o">-</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">names</span><span class="p">[</span><span class="n">length</span><span class="o">-</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">],</span> <span class="n">names</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> +<span class="o">...</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">names</span> +<span class="p">[</span><span class="s1">&#39;elixir&#39;</span><span class="p">,</span> <span class="s1">&#39;rust&#39;</span><span class="p">,</span> <span class="s1">&#39;go&#39;</span><span class="p">,</span> <span class="s1">&#39;python&#39;</span><span class="p">,</span> <span class="s1">&#39;php&#39;</span><span class="p">,</span> <span class="s1">&#39;js&#39;</span><span class="p">,</span> <span class="s1">&#39;java&#39;</span><span class="p">,</span> <span class="s1">&#39;c&#39;</span><span class="p">]</span> +</code></pre></div> + +<h3>Kết&nbsp;luận</h3> +<p>slice với step=-1 có diễn biến bất thường. Nếu cần tìm xuống bên dưới thì đọc code CPython, nhỡ mà khó quá, thì qua&nbsp;MicroPython.</p> +<p><span class="caps">PS</span>: nhớ làm bài tập với&nbsp;CPython.</p>Lớp Python PyMivn khóa 46 tại Hà Nội khai giảng 7/3/20242024-02-25T00:00:00+07:002024-02-25T00:00:00+07:00Pymier0tag:n.pymi.vn,2024-02-25:/pymi2403.html<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để trở thành lập trình viên #python chuyên&nbsp;nghiệp.</p> +<p><img alt="img" src="https://n.pymi.vn/images/hn2110.jpg"></p> +<p>Học viên không ở Hà Nội có thể học qua livestream trên Youtube với kênh chat +Slack cùng các học viên Hà&nbsp;Nội.</p> +<p>Chi tiết tại trang chủ <a href="https://pymi.vn">Pymivn</a>. +Thắc mắc/hỏi đáp: <a href="https://invite.pymi.vn">slack</a></p>pygame - vẽ logo Windows2024-01-28T00:00:00+07:002024-01-28T00:00:00+07:00Pymier0tag:n.pymi.vn,2024-01-28:/pygame-draw.html<p>Pygame là thư viện làm game với Python, nhưng dùng để vẽ thì cũng không ai phản đối cả. +Lập trình viên JavaScript dễ dàng vẽ hình tròn, vuông với vài dòng code thì với lập trình viên Python, thế giới chỉ có màn hình đen chữ trắng, cần thêm …</p><p>Pygame là thư viện làm game với Python, nhưng dùng để vẽ thì cũng không ai phản đối cả. +Lập trình viên JavaScript dễ dàng vẽ hình tròn, vuông với vài dòng code thì với lập trình viên Python, thế giới chỉ có màn hình đen chữ trắng, cần thêm chút &#8220;sắc&nbsp;màu&#8221;.</p> +<h3>Cài đặt&nbsp;pygame</h3> +<div class="highlight"><pre><span></span><code>pip install pygame +</code></pre></div> + +<h3>Khái niệm main loop, surface, blit,&nbsp;framerate</h3> +<ul> +<li>Surface: Mỗi &#8220;hiển thị&#8221; trên cửa sổ pygame gọi là Surface. Vẽ surface theo tọa độ (x,y) với góc trên bên trái là (0,&nbsp;0).</li> +<li>Framerate: Clock tick 60 gọi là framerate, ở đây sẽ hiển thị 60 hình mỗi&nbsp;giây.</li> +<li>blit: draw one image onto&nbsp;another</li> +</ul> +<p>Bộ màu lấy từ <a href="https://colorhunt.co">https://colorhunt.co</a> thay vì dùng logo của Windows để tránh vấn đề bản&nbsp;quyền.</p> +<h3>Code</h3> +<p><img alt="A flag" src="https://n.pymi.vn/images/pygame_flag.png"></p> +<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">pygame</span> + +<span class="n">pygame</span><span class="o">.</span><span class="n">init</span><span class="p">()</span> + +<span class="n">screen</span> <span class="o">=</span> <span class="n">pygame</span><span class="o">.</span><span class="n">display</span><span class="o">.</span><span class="n">set_mode</span><span class="p">((</span><span class="mi">800</span><span class="p">,</span> <span class="mi">600</span><span class="p">))</span> +<span class="n">clock</span> <span class="o">=</span> <span class="n">pygame</span><span class="o">.</span><span class="n">time</span><span class="o">.</span><span class="n">Clock</span><span class="p">()</span> + +<span class="c1"># color pallete from https://colorhunt.co/palette/f38181fce38aeaffd095e1d3</span> +<span class="n">c1</span> <span class="o">=</span> <span class="n">pygame</span><span class="o">.</span><span class="n">Surface</span><span class="p">((</span><span class="mi">400</span><span class="p">,</span><span class="mi">300</span><span class="p">))</span> +<span class="n">c1</span><span class="o">.</span><span class="n">fill</span><span class="p">((</span><span class="mi">243</span><span class="p">,</span> <span class="mi">129</span><span class="p">,</span> <span class="mi">129</span><span class="p">))</span> +<span class="n">c2</span> <span class="o">=</span> <span class="n">pygame</span><span class="o">.</span><span class="n">Surface</span><span class="p">((</span><span class="mi">400</span><span class="p">,</span><span class="mi">300</span><span class="p">))</span> +<span class="n">c2</span><span class="o">.</span><span class="n">fill</span><span class="p">((</span><span class="mi">252</span><span class="p">,</span> <span class="mi">227</span><span class="p">,</span> <span class="mi">138</span><span class="p">))</span> + +<span class="n">c3</span> <span class="o">=</span> <span class="n">pygame</span><span class="o">.</span><span class="n">Surface</span><span class="p">((</span><span class="mi">400</span><span class="p">,</span><span class="mi">300</span><span class="p">))</span> +<span class="n">c3</span><span class="o">.</span><span class="n">fill</span><span class="p">((</span><span class="mi">234</span><span class="p">,</span> <span class="mi">255</span><span class="p">,</span> <span class="mi">208</span><span class="p">))</span> + +<span class="n">c4</span> <span class="o">=</span> <span class="n">pygame</span><span class="o">.</span><span class="n">Surface</span><span class="p">((</span><span class="mi">400</span><span class="p">,</span><span class="mi">300</span><span class="p">))</span> +<span class="n">c4</span><span class="o">.</span><span class="n">fill</span><span class="p">((</span><span class="mi">149</span><span class="p">,</span> <span class="mi">225</span><span class="p">,</span> <span class="mi">211</span><span class="p">))</span> + +<span class="c1">#logo = pygame.image.load(&quot;graphics/Mi_200_200.jpeg&quot;).convert_alpha()</span> + +<span class="n">pygame</span><span class="o">.</span><span class="n">display</span><span class="o">.</span><span class="n">set_caption</span><span class="p">(</span><span class="s2">&quot;GAMI&quot;</span><span class="p">)</span> + +<span class="k">while</span> <span class="kc">True</span><span class="p">:</span> + <span class="k">for</span> <span class="n">e</span> <span class="ow">in</span> <span class="n">pygame</span><span class="o">.</span><span class="n">event</span><span class="o">.</span><span class="n">get</span><span class="p">():</span> + <span class="k">if</span> <span class="n">e</span><span class="o">.</span><span class="n">type</span> <span class="o">==</span> <span class="n">pygame</span><span class="o">.</span><span class="n">QUIT</span><span class="p">:</span> + <span class="n">pygame</span><span class="o">.</span><span class="n">quit</span><span class="p">()</span> + <span class="n">exit</span><span class="p">()</span> + <span class="n">screen</span><span class="o">.</span><span class="n">blit</span><span class="p">(</span><span class="n">c1</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="n">screen</span><span class="o">.</span><span class="n">blit</span><span class="p">(</span><span class="n">c2</span><span class="p">,</span> <span class="p">(</span><span class="mi">400</span><span class="p">,</span> <span class="mi">0</span><span class="p">))</span> + <span class="n">screen</span><span class="o">.</span><span class="n">blit</span><span class="p">(</span><span class="n">c3</span><span class="p">,</span> <span class="p">(</span><span class="mi">400</span><span class="p">,</span> <span class="mi">300</span><span class="p">))</span> + <span class="n">screen</span><span class="o">.</span><span class="n">blit</span><span class="p">(</span><span class="n">c4</span><span class="p">,</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">300</span><span class="p">))</span> + + <span class="c1">#screen.blit(logo, (300, 200))</span> + + <span class="n">pygame</span><span class="o">.</span><span class="n">display</span><span class="o">.</span><span class="n">update</span><span class="p">()</span> + <span class="n">clock</span><span class="o">.</span><span class="n">tick</span><span class="p">(</span><span class="mi">60</span><span class="p">)</span> +</code></pre></div> + +<h3>Kết&nbsp;luận</h3> +<p>Pygame giúp vẽ dễ dàng không kém gì JavaScript trên trình&nbsp;duyệt.</p> +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>mime trên MacOS không như mime trên Ubuntu2023-10-11T00:00:00+07:002023-10-11T00:00:00+07:00Pymier0tag:n.pymi.vn,2023-10-11:/mime.html<p>Viết code chạy trên được nhiều hệ điều hành gọi là &#8220;cross-platform&#8221;, là lý do mà +lập trình viên sẽ dùng các thư viện thay vì&nbsp;&#8220;hard-code&#8221;.</p> +<h3>Cross-platform file path với&nbsp;os</h3> +<p>Ví dụ đường dẫn thư mục &#8220;a&#8221; chứa thư mục &#8220;b&#8221;: trên Linux/MacOS: <code>a/b</code> thì trên …</p><p>Viết code chạy trên được nhiều hệ điều hành gọi là &#8220;cross-platform&#8221;, là lý do mà +lập trình viên sẽ dùng các thư viện thay vì&nbsp;&#8220;hard-code&#8221;.</p> +<h3>Cross-platform file path với&nbsp;os</h3> +<p>Ví dụ đường dẫn thư mục &#8220;a&#8221; chứa thư mục &#8220;b&#8221;: trên Linux/MacOS: <code>a/b</code> thì trên Windows là <code>a\b</code>. +Sử dụng thư viện như <code>os</code> sẽ giải quyết các sự khác biệt phía&nbsp;dưới.</p> +<p>Trên&nbsp;Linux</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">os</span> +<span class="o">&gt;&gt;&gt;</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="s2">&quot;a&quot;</span><span class="p">,</span> <span class="s2">&quot;b&quot;</span><span class="p">)</span> +<span class="s1">&#39;a/b&#39;</span> +</code></pre></div> + +<h3><span class="caps">MIME</span> - Multipurpose Internet Mail&nbsp;Extensions</h3> +<blockquote> +<p>Multipurpose Internet Mail Extensions (<span class="caps">MIME</span>) is an Internet standard that +extends the format of email messages to support text in character sets other +than <span class="caps">ASCII</span>, as well as attachments of audio, video, images, and application&nbsp;programs.</p> +</blockquote> +<p><a href="https://en.wikipedia.org/wiki/MIME">https://en.wikipedia.org/wiki/<span class="caps">MIME</span></a></p> +<p>Lúc mới có máy tính, email chỉ để gửi text, sau này được mở rộng ra để gửi ảnh, +nhạc, video, game&#8230; <span class="caps">MIME</span> là tên tiêu chuẩn để hỗ trợ việc mở rộng&nbsp;này.</p> +<p>Thư viện <code>mimetypes</code> trong standard lib của Python giúp đoán &#8220;<span class="caps">MIME</span> type&#8221; và +encoding của 1 <span class="caps">URL</span>/filename.</p> +<p>Trên&nbsp;MacOS</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">mimetype</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">mimetypes</span><span class="o">.</span><span class="n">guess_type</span><span class="p">(</span><span class="s2">&quot;nginx.log.gz&quot;</span><span class="p">)</span> +<span class="p">(</span><span class="s1">&#39;text/plain&#39;</span><span class="p">,</span> <span class="s1">&#39;gzip&#39;</span><span class="p">)</span> +</code></pre></div> + +<p>Trên Ubuntu server (hay <code>docker run -it python:3.8.18</code>)</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">mimetypes</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">mimetypes</span><span class="o">.</span><span class="n">guess_type</span><span class="p">(</span><span class="s2">&quot;nginx.log.gz&quot;</span><span class="p">)</span> +<span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="s1">&#39;gzip&#39;</span><span class="p">)</span> +</code></pre></div> + +<p>Có vẻ như kết quả khác nhau là do hệ điều&nbsp;hành?</p> +<p>Dùng strace xem mimetypes đọc thông tin từ&nbsp;đâu:</p> +<div class="highlight"><pre><span></span><code>$ strace -e openat python3 mime.py +... +openat<span class="o">(</span>AT_FDCWD, <span class="s2">&quot;/etc/mime.types&quot;</span>, O_RDONLY<span class="p">|</span>O_CLOEXEC<span class="o">)</span> <span class="o">=</span> <span class="m">3</span> +<span class="o">(</span><span class="s1">&#39;text/plain&#39;</span>, <span class="s1">&#39;gzip&#39;</span><span class="o">)</span> ++++ exited with <span class="m">0</span> +++ +</code></pre></div> + +<p>Tìm xem gói nào cài&nbsp;/etc/mime.types:</p> +<div class="highlight"><pre><span></span><code>$ dpkg -S /etc/mime.types +media-types: /etc/mime.types +$ apt-cache show media-types +Priority: important +... +Task: minimal, server-minimal +</code></pre></div> + +<p>Gói này rất quan trọng và có sẵn trên server Ubuntu, kể cả trong python:3.8.18 Docker image cũng&nbsp;có:</p> +<div class="highlight"><pre><span></span><code>$ dpkg -l media-types +ii media-types <span class="m">10</span>.0.0 all List of standard media types and their usual file extension +</code></pre></div> + +<p>Nhưng trong file <code>/etc/mime.types</code> trên Ubuntu 22.04, docker fedora:38, docker python:3.8.18 chạy debian 12 đều có kết quả như&nbsp;nhau:</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">mimetypes</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">mimetypes</span><span class="o">.</span><span class="n">guess_type</span><span class="p">(</span><span class="s2">&quot;nginx.log.gz&quot;</span><span class="p">)</span> +<span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="s1">&#39;gzip&#39;</span><span class="p">)</span> +</code></pre></div> + +<p>Do trong /etc/mime.types không chứa dòng nào cho <code>log</code></p> +<div class="highlight"><pre><span></span><code># grep &#39; log&#39; /etc/mime.types -c +0 +</code></pre></div> + +<p>Trên docker <code>python:3.8.18-alpine</code> thậm chí còn không có file&nbsp;/etc/mime.types</p> +<div class="highlight"><pre><span></span><code>$ docker run -it python:3.8.18-alpine sh -c <span class="s1">&#39;ls -l /etc/mime.types&#39;</span> +ls: /etc/mime.types: No such file or directory +</code></pre></div> + +<p>Trên Manjaro 23 desktop, mở <code>/etc/mime.types</code> tìm&nbsp;thấy:</p> +<div class="highlight"><pre><span></span><code>$ grep text/plain /etc/mime.types +text/plain txt asc text pm el c h cc hh cxx hxx f90 conf log +</code></pre></div> + +<p>chạy code cho kết&nbsp;quả:</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">mimetypes</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">mimetypes</span><span class="o">.</span><span class="n">guess_type</span><span class="p">(</span><span class="s2">&quot;nginx.log.gz&quot;</span><span class="p">)</span> +<span class="p">(</span><span class="s1">&#39;text/plain&#39;</span><span class="p">,</span> <span class="s1">&#39;gzip&#39;</span><span class="p">)</span> +</code></pre></div> + +<p>Vậy kết quả của function này phụ thuộc vào nội dung của các file&nbsp;&#8220;mime.types&#8221;.</p> +<p>Thư viện mimetypes sẽ tìm trong các&nbsp;files:</p> +<div class="highlight"><pre><span></span><code><span class="n">knownfiles</span> <span class="o">=</span> <span class="p">[</span> + <span class="s2">&quot;/etc/mime.types&quot;</span><span class="p">,</span> + <span class="s2">&quot;/etc/httpd/mime.types&quot;</span><span class="p">,</span> <span class="c1"># Mac OS X</span> + <span class="s2">&quot;/etc/httpd/conf/mime.types&quot;</span><span class="p">,</span> <span class="c1"># Apache</span> + <span class="s2">&quot;/etc/apache/mime.types&quot;</span><span class="p">,</span> <span class="c1"># Apache 1</span> + <span class="s2">&quot;/etc/apache2/mime.types&quot;</span><span class="p">,</span> <span class="c1"># Apache 2</span> + <span class="s2">&quot;/usr/local/etc/httpd/conf/mime.types&quot;</span><span class="p">,</span> + <span class="s2">&quot;/usr/local/lib/netscape/mime.types&quot;</span><span class="p">,</span> + <span class="s2">&quot;/usr/local/etc/httpd/conf/mime.types&quot;</span><span class="p">,</span> <span class="c1"># Apache 1.2</span> + <span class="s2">&quot;/usr/local/etc/mime.types&quot;</span><span class="p">,</span> <span class="c1"># Apache 1.3</span> +<span class="p">]</span> +</code></pre></div> + +<p><a href="https://github.com/python/cpython/blob/3.12/Lib/mimetypes.py#L48-L58">https://github.com/python/cpython/blob/3.12/Lib/mimetypes.py#L48-L58</a></p> +<h3>Kết&nbsp;luận</h3> +<p>Một ví dụ nữa cho việc &#8220;code chạy trên máy tôi&#8221; nhưng không chạy khi deploy thật trên server. +Dev trên MacOS deploy trên Ubuntu/Container vẫn có đầy đau thương, và dùng &#8220;container&#8221; (docker) khi dev trên local là 1 giải&nbsp;pháp.</p> +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Lộ mật khẩu cùng dataclass2023-08-30T00:00:00+07:002023-08-30T00:00:00+07:00Pymier0tag:n.pymi.vn,2023-08-30:/dataclass-leak.html<p><code>dataclasses</code> module là 1 standard library rất mới, xuất hiện ở Python 3.7, với tác&nbsp;dụng:</p> +<blockquote> +<p>This module provides a decorator and functions for automatically adding generated special methods such as <code>__init__()</code> and <code>__repr__()</code> to user-defined&nbsp;classes.</p> +</blockquote> +<p><a href="https://peps.python.org/pep-0557/">https://peps.python.org/pep-0557/</a></p> +<p>đây là một tính năng được yêu …</p><p><code>dataclasses</code> module là 1 standard library rất mới, xuất hiện ở Python 3.7, với tác&nbsp;dụng:</p> +<blockquote> +<p>This module provides a decorator and functions for automatically adding generated special methods such as <code>__init__()</code> and <code>__repr__()</code> to user-defined&nbsp;classes.</p> +</blockquote> +<p><a href="https://peps.python.org/pep-0557/">https://peps.python.org/pep-0557/</a></p> +<p>đây là một tính năng được yêu thích ở các bản Python mới&nbsp;(3.7+).</p> +<h3>dataclass giúp viết class ngắn hơn, không phải tự gõ <code>__init__</code></h3> +<p><code>dataclass</code> là một decorator, dùng trên đầu&nbsp;class:</p> +<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">dataclasses</span> <span class="kn">import</span> <span class="n">dataclass</span> + +<span class="nd">@dataclass</span> +<span class="k">class</span> <span class="nc">User</span><span class="p">:</span> + <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> + <span class="n">age</span><span class="p">:</span> <span class="nb">int</span> + <span class="n">email</span><span class="p">:</span> <span class="nb">str</span> + +<span class="n">u</span> <span class="o">=</span> <span class="n">User</span><span class="p">(</span><span class="s2">&quot;Pymier&quot;</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="s2">&quot;hvn@pymi.vn&quot;</span><span class="p">)</span> +<span class="nb">print</span><span class="p">(</span><span class="n">u</span><span class="p">)</span> +<span class="c1"># User(name=&#39;Pymier&#39;, age=8, email=&#39;hvn@pymi.vn&#39;)</span> +</code></pre></div> + +<p>Tiện vậy có gì mà không&nbsp;tốt?</p> +<h3>dataclass luôn in ra mọi&nbsp;attribute</h3> +<p>Mặc định, khi print một object tạo bởi class dùng dataclass, nó sẽ in ra mọi attribute. +Giả sử một ngày, cần chứa mật khẩu trong class User, đoạn code trở&nbsp;thành:</p> +<div class="highlight"><pre><span></span><code><span class="nd">@dataclass</span> +<span class="k">class</span> <span class="nc">User</span><span class="p">:</span> + <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> + <span class="n">age</span><span class="p">:</span> <span class="nb">int</span> + <span class="n">email</span><span class="p">:</span> <span class="nb">str</span> + <span class="n">password</span><span class="p">:</span> <span class="nb">str</span> + +<span class="n">u</span> <span class="o">=</span> <span class="n">User</span><span class="p">(</span><span class="s2">&quot;Pymier&quot;</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="s2">&quot;hvn@pymi.vn&quot;</span><span class="p">,</span> <span class="s2">&quot;hunter42&quot;</span><span class="p">)</span> +<span class="nb">print</span><span class="p">(</span><span class="n">u</span><span class="p">)</span> +<span class="c1"># User(name=&#39;Pymier&#39;, age=8, email=&#39;hvn@pymi.vn&#39;, password=&#39;hunter42&#39;)</span> +</code></pre></div> + +<p>Vậy là nhờ dùng dataclass, ta VÔ TÌNH để lộ mật khẩu, in ra màn hình, render ra website hay theo log file lộ ra&nbsp;ngoài&#8230;</p> +<h4>Sửa lại thế&nbsp;nào?</h4> +<p>muốn print object ra gì, rõ là phải sửa <code>__str__</code>!, thêm method <code>__str__</code> vào</p> +<div class="highlight"><pre><span></span><code> <span class="o">...</span> + <span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span> + <span class="k">return</span> <span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">=}</span><span class="s2"> </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">age</span><span class="si">=}</span><span class="s2"> </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">email</span><span class="si">=}</span><span class="s2">&quot;</span> + +<span class="n">u</span> <span class="o">=</span> <span class="n">User</span><span class="p">(</span><span class="s2">&quot;Pymier&quot;</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="s2">&quot;hvn@pymi.vn&quot;</span><span class="p">,</span> <span class="s2">&quot;hunter42&quot;</span><span class="p">)</span> +<span class="nb">print</span><span class="p">(</span><span class="n">u</span><span class="p">)</span> +<span class="c1"># self.name=&#39;Pymier&#39; self.age=8 self.email=&#39;hvn@pymi.vn&#39;</span> +</code></pre></div> + +<p>Kết quả giờ không còn hiện ra password nữa, ta lặng lẽ ỉm đi cái security bug này và không ai biết tới, thật tuyệt&nbsp;vời!</p> +<p>Xong chưa? nên dừng lại 1 chút suy nghĩ rồi hãy đọc&nbsp;tiếp.</p> +<p>Đoạn code ban đầu hoàn hảo, được thiết kế chủ ý dùng dataclass vì có thể in ra mọi thông tin, thì sau 1 thay đổi &#8220;nhỏ&#8221; và thường là do 1 người khác thay đổi, trở thành code có lỗ hổng bảo mật (security vulnerability). +Người ta thường so sánh ngành lập trình với ngành xây dựng, software thì toàn bug còn nhà thì mấy khi &#8220;hỏng&#8221;? software bị thay đổi hàng ngày, còn nhà thì vài năm hay thập kỷ mới&nbsp;thay.</p> +<h3>Tạo 1 class mới chứa dataclass&nbsp;này</h3> +<p>Tạo 1 team class dùng dataclass, và in&nbsp;ra:</p> +<div class="highlight"><pre><span></span><code><span class="nd">@dataclass</span> +<span class="k">class</span> <span class="nc">Team</span><span class="p">:</span> + <span class="n">members</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">User</span><span class="p">]</span> + +<span class="n">t</span> <span class="o">=</span> <span class="n">Team</span><span class="p">(</span><span class="n">members</span><span class="o">=</span><span class="p">[</span><span class="n">u</span><span class="p">])</span> +<span class="nb">print</span><span class="p">(</span><span class="n">t</span><span class="p">)</span> +<span class="c1"># Team(members=[User(name=&#39;Pymier&#39;, age=8, email=&#39;hvn@pymi.vn&#39;, password=&#39;hunter42&#39;)])</span> +</code></pre></div> + +<p>dù ta đã viết <code>__str__</code> cho <code>User</code>, nhưng khi in ra <code>Team</code> object, nó sử dụng <code>__repr__</code> chứ không dùng <code>__str__</code>, như dòng đầu tiên trong tài liệu của <code>dataclasses</code> đã&nbsp;viết:</p> +<blockquote> +<p>automatically adding generated special methods such as <code>__init__()</code> <strong>and</strong> <code>__repr__()</code> to user-defined&nbsp;classes</p> +</blockquote> +<p>Cách fix: thêm <code>__repr__</code>:</p> +<blockquote> +<p>If a class defines <code>__repr__()</code> but not <code>__str__()</code>, then <code>__repr__()</code> is also used when an “informal” string representation of instances of that class is&nbsp;required.</p> +</blockquote> +<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">dataclasses</span> <span class="kn">import</span> <span class="n">dataclass</span> + +<span class="nd">@dataclass</span> +<span class="k">class</span> <span class="nc">User</span><span class="p">:</span> + <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> + <span class="n">age</span><span class="p">:</span> <span class="nb">int</span> + <span class="n">email</span><span class="p">:</span> <span class="nb">str</span> + <span class="n">password</span><span class="p">:</span> <span class="nb">str</span> + <span class="k">def</span> <span class="fm">__repr__</span><span class="p">(</span><span class="bp">self</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span> + <span class="k">return</span> <span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">name</span><span class="si">=}</span><span class="s2"> </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">age</span><span class="si">=}</span><span class="s2"> </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">email</span><span class="si">=}</span><span class="s2">&quot;</span> + +<span class="n">u</span> <span class="o">=</span> <span class="n">User</span><span class="p">(</span><span class="s2">&quot;Pymier&quot;</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="s2">&quot;hvn@pymi.vn&quot;</span><span class="p">,</span> <span class="s2">&quot;hunter42&quot;</span><span class="p">)</span> +<span class="nb">print</span><span class="p">(</span><span class="n">u</span><span class="p">)</span> +<span class="c1"># self.name=&#39;Pymier&#39; self.age=8 self.email=&#39;hvn@pymi.vn&#39;</span> +<span class="nd">@dataclass</span> +<span class="k">class</span> <span class="nc">Team</span><span class="p">:</span> + <span class="n">members</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="n">User</span><span class="p">]</span> + +<span class="n">t</span> <span class="o">=</span> <span class="n">Team</span><span class="p">(</span><span class="n">members</span><span class="o">=</span><span class="p">[</span><span class="n">u</span><span class="p">])</span> +<span class="nb">print</span><span class="p">(</span><span class="n">t</span><span class="p">)</span> +<span class="c1"># Team(members=[self.name=&#39;Pymier&#39; self.age=8 self.email=&#39;hvn@pymi.vn&#39;])</span> +</code></pre></div> + +<p>Bạn đọc tham khảo sự khác biệt giữa <code>__str__</code> và <code>__repr__</code>: <a href="https://stackoverflow.com/questions/1436703/what-is-the-difference-between-str-and-repr">https://stackoverflow.com/questions/1436703/what-is-the-difference-between-str-and-repr</a></p> +<p>Bất ngờ thay, mấy ông già không &#8220;cool&#8221; không &#8220;trend&#8221; không dùng <code>dataclass</code> không gặp phải vấn đề&nbsp;này:</p> +<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">dataclasses</span> <span class="kn">import</span> <span class="n">dataclass</span> + +<span class="k">class</span> <span class="nc">User</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">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">age</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">email</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">password</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span> + <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span> + <span class="bp">self</span><span class="o">.</span><span class="n">age</span> <span class="o">=</span> <span class="n">age</span> + <span class="bp">self</span><span class="o">.</span><span class="n">email</span> <span class="o">=</span> <span class="n">email</span> + <span class="bp">self</span><span class="o">.</span><span class="n">password</span> <span class="o">=</span> <span class="n">password</span> + +<span class="n">u</span> <span class="o">=</span> <span class="n">User</span><span class="p">(</span><span class="s2">&quot;Pymier&quot;</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="s2">&quot;hvn@pymi.vn&quot;</span><span class="p">,</span> <span class="s2">&quot;hunter42&quot;</span><span class="p">)</span> +<span class="nb">print</span><span class="p">(</span><span class="n">u</span><span class="p">,</span> <span class="nb">repr</span><span class="p">(</span><span class="n">u</span><span class="p">))</span> +<span class="c1"># &lt;__main__.User object at 0x7f9efd510410&gt; &lt;__main__.User object at 0x7f9efd510410&gt;</span> +</code></pre></div> + +<p>Update: dataclasses cũng đã tính đến chuyện này, để KHÔNG hiện một attribute khi <code>__repr__</code>, dùng <code>field(repr=False)</code></p> +<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">dataclasses</span> <span class="kn">import</span> <span class="n">dataclass</span><span class="p">,</span> <span class="n">field</span> + +<span class="nd">@dataclass</span> +<span class="k">class</span> <span class="nc">User</span><span class="p">:</span> + <span class="n">name</span><span class="p">:</span> <span class="nb">str</span> + <span class="n">age</span><span class="p">:</span> <span class="nb">int</span> + <span class="n">email</span><span class="p">:</span> <span class="nb">str</span> + <span class="n">password</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">field</span><span class="p">(</span><span class="nb">repr</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> +<span class="n">u</span> <span class="o">=</span> <span class="n">User</span><span class="p">(</span><span class="s2">&quot;Pymier&quot;</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="s2">&quot;hvn@pymi.vn&quot;</span><span class="p">,</span> <span class="s2">&quot;hunter42&quot;</span><span class="p">)</span> +<span class="nb">print</span><span class="p">(</span><span class="n">u</span><span class="p">)</span> +<span class="c1"># User(name=&#39;Pymier&#39;, age=8, email=&#39;hvn@pymi.vn&#39;)</span> +</code></pre></div> + +<h3>Tham&nbsp;khảo</h3> +<p><a href="https://docs.python.org/3.11/library/dataclasses.html">https://docs.python.org/3.11/library/dataclasses.html</a></p> +<p>Xem list các method được dataclass autogenerate tại <a href="https://peps.python.org/pep-0557/#abstract">https://peps.python.org/pep-0557/#abstract</a></p> +<h3>Kết&nbsp;luận</h3> +<p><code>dataclasses</code> tiện lợi, giúp viết ít code hơn, nhưng cần biết những gì nó &#8220;tự động&#8221; để tránh bất ngờ. +Default không có nghĩa là không biết. Explicit is better than&nbsp;implicit.</p> +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Giải bài toán tháp Hà Nội dùng đệ quy2023-08-19T00:00:00+07:002023-08-19T00:00:00+07:00Pymier0tag:n.pymi.vn,2023-08-19:/towerofhanoi.html<p>Tower of Hanoi là một bài toán kinh điển trong ngành khoa học máy tính, được dạy ở mọi trường đại học dạy <span class="caps">CNTT</span> ở Việt Nam trong môn &#8220;Toán Rời&nbsp;Rạc&#8221;.</p> +<h3>Tower of Hanoi không phải của người&nbsp;Việt</h3> +<p>Tower of Hanoi được phát minh bởi nhà toán học …</p><p>Tower of Hanoi là một bài toán kinh điển trong ngành khoa học máy tính, được dạy ở mọi trường đại học dạy <span class="caps">CNTT</span> ở Việt Nam trong môn &#8220;Toán Rời&nbsp;Rạc&#8221;.</p> +<h3>Tower of Hanoi không phải của người&nbsp;Việt</h3> +<p>Tower of Hanoi được phát minh bởi nhà toán học người Pháp Édouard Lucas năm 1883 và ông này không có gốc&nbsp;Việt.</p> +<p>Bài toán phát biểu như&nbsp;sau:</p> +<ul> +<li>Cho 3 cái cột, cần chuyển các đĩa từ cột 1 sang cột 3, thông qua cột&nbsp;2</li> +<li>Mỗi lần chỉ được chuyển 1 đĩa, đĩa bên dưới phải to hơn đĩa bên&nbsp;trên.</li> +</ul> +<h3>Bài&nbsp;giải</h3> +<p>Hình ảnh minh họa sinh động bài&nbsp;giải:</p> +<p><img alt="solve Tower of Hanoi" src="https://upload.wikimedia.org/wikipedia/commons/6/60/Tower_of_Hanoi_4.gif"></p> +<p>Phân tích cách viết function đệ&nbsp;quy:</p> +<p>Đặt 3 cột là source (nguồn), dest (đích), tmp (trung gian), function tên move, ban đầu có thể nghĩ function trông như&nbsp;sau:</p> +<div class="highlight"><pre><span></span><code><span class="n">move</span><span class="p">(</span><span class="n">source</span><span class="p">,</span> <span class="n">dest</span><span class="p">,</span> <span class="n">tmp</span><span class="p">)</span> +</code></pre></div> + +<p>Khi viết recursive function, thường sử dụng các argument để lưu trạng thái (state) function. +Bài toán này có 1 trạng thái là số lượng đĩa còn lại cần chuyển, ban đầu có giá trị là n, khi kết thúc là 0. +Sau mỗi bước chuyển, trạng thái của bài toán được lưu giữ trong số đĩa còn lại và nội dung của mỗi cột. +Vậy function sẽ trở&nbsp;thành</p> +<div class="highlight"><pre><span></span><code><span class="n">move</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">source</span><span class="p">,</span> <span class="n">dest</span><span class="p">,</span> <span class="n">tmp</span><span class="p">)</span> +</code></pre></div> + +<p>Có thể cho rằng chỉ cần 3 cột cũng đủ biểu diễn trạng thái của chương trình, không cần tới <code>n</code>, và dùng <code>len(source)</code> để tính số đĩa còn lại, nhưng khi giải, các cột lần lượt thay đổi vai trò của nhau khiến không thể biết cột nào là cột <code>source</code> ban&nbsp;đầu.</p> +<div class="highlight"><pre><span></span><code><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">2</span><span class="p">,</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="n">C</span> <span class="o">=</span> <span class="p">[]</span> +<span class="k">def</span> <span class="nf">move</span><span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">source</span><span class="p">,</span> <span class="n">dest</span><span class="p">,</span> <span class="n">tmp</span><span class="p">):</span> + <span class="k">if</span> <span class="n">n</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span> + <span class="n">move</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">source</span><span class="p">,</span> <span class="n">tmp</span><span class="p">,</span> <span class="n">dest</span><span class="p">)</span> + <span class="n">dest</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">source</span><span class="o">.</span><span class="n">pop</span><span class="p">())</span> + <span class="nb">print</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">C</span><span class="p">,</span> <span class="s2">&quot;#####&quot;</span><span class="p">)</span> + <span class="n">move</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">tmp</span><span class="p">,</span> <span class="n">dest</span><span class="p">,</span> <span class="n">source</span><span class="p">)</span> + +<span class="n">move</span><span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="n">A</span><span class="p">,</span> <span class="n">C</span><span class="p">,</span> <span class="n">B</span><span class="p">)</span> +</code></pre></div> + +<p>(code theo <a href="https://en.wikipedia.org/w/index.php?title=Tower_of_Hanoi&amp;oldid=1139870102#Recursive_implementation">https://en.wikipedia.org/w/index.php?title=Tower_of_Hanoi&amp;oldid=1139870102#Recursive_implementation</a>)</p> +<p>Bài toán dừng lại khi n = 0, để move đĩa 3 từ A sang&nbsp;C:</p> +<ul> +<li>cần move 2 đĩa trên từ A sang B, dùng C làm trung&nbsp;gian</li> +<li>rồi chuyển đĩa 3 từ A sang&nbsp;C</li> +<li>sau đó, chuyển 2 đĩa từ B sang C sử dụng A làm trung&nbsp;gian.</li> +</ul> +<p>Chú ý <code>print</code> A B C chứ không in ra source, dest, tmp, bởi dùng A B C mới giữ được thứ tự các&nbsp;cột.</p> +<div class="highlight"><pre><span></span><code>[3, 2] [] [1] ##### +[3] [2] [1] ##### +[3] [2, 1] [] ##### +[] [2, 1] [3] ##### +[1] [2] [3] ##### +[1] [] [3, 2] ##### +[] [] [3, 2, 1] ##### +</code></pre></div> + +<h3>Tham&nbsp;khảo</h3> +<p><a href="https://en.wikipedia.org/wiki/Tower_of_Hanoi">https://en.wikipedia.org/wiki/Tower_of_Hanoi</a></p> +<h3>Kết&nbsp;luận</h3> +<p>Tháp Hà Nội ở đó, chuyển đi&nbsp;đâu?</p> +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Lớp Python PyMivn khóa 45 tại Hà Nội khai giảng 27/7/20232023-07-23T00:00:00+07:002023-07-23T00:00:00+07:00Pymier0tag:n.pymi.vn,2023-07-23:/pymi2307.html<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để trở thành lập trình viên #python chuyên&nbsp;nghiệp.</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1682686581854-5e71f58e7e3f?ixlib=rb-4.0.3&amp;q=85&amp;fm=jpg&amp;crop=entropy&amp;cs=srgb&amp;dl=neom-wUyMk7ziLT0-unsplash.jpg&amp;w=640"></p> +<p>Photo by <a href="https://unsplash.com/@neom?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText"><span class="caps">NEOM</span></a> on <a href="https://unsplash.com/photos/wUyMk7ziLT0?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></p> +<p>Học viên không ở Hà Nội có thể học qua livestream trên Youtube với kênh chat +Slack cùng các học viên Hà&nbsp;Nội.</p> +<p>Chi tiết tại trang chủ <a href="https://pymi.vn">Pymivn</a>. +Thắc …</p><p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để trở thành lập trình viên #python chuyên&nbsp;nghiệp.</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1682686581854-5e71f58e7e3f?ixlib=rb-4.0.3&amp;q=85&amp;fm=jpg&amp;crop=entropy&amp;cs=srgb&amp;dl=neom-wUyMk7ziLT0-unsplash.jpg&amp;w=640"></p> +<p>Photo by <a href="https://unsplash.com/@neom?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText"><span class="caps">NEOM</span></a> on <a href="https://unsplash.com/photos/wUyMk7ziLT0?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></p> +<p>Học viên không ở Hà Nội có thể học qua livestream trên Youtube với kênh chat +Slack cùng các học viên Hà&nbsp;Nội.</p> +<p>Chi tiết tại trang chủ <a href="https://pymi.vn">Pymivn</a>. +Thắc mắc/hỏi đáp: <a href="https://invite.pymi.vn">slack</a></p>Django template gọi method không cần ()2023-07-16T00:00:00+07:002023-07-16T00:00:00+07:00Pymier0tag:n.pymi.vn,2023-07-16:/django-template.html<p>Python có 2 template engine phổ biến nhất: Jinja2 (hay dùng với Flask, SaltStack, Ansible&#8230;) và Django&nbsp;template.</p> +<p>Cả 2 khá giống nhau về cú pháp: viết for/if như Python, dùng function qua các filter có sẵn, đoạn code sau chạy +cho cả&nbsp;2:</p> +<div class="highlight"><pre><span></span><code><span class="p">{</span><span class="o">%</span> <span class="k">for</span> <span class="n">fw</span> <span class="ow">in</span> <span class="n">frameworks</span> <span class="o">%</span><span class="p">}</span> + <span class="p">{</span><span class="o">%</span> <span class="k">if …</span></code></pre></div><p>Python có 2 template engine phổ biến nhất: Jinja2 (hay dùng với Flask, SaltStack, Ansible&#8230;) và Django&nbsp;template.</p> +<p>Cả 2 khá giống nhau về cú pháp: viết for/if như Python, dùng function qua các filter có sẵn, đoạn code sau chạy +cho cả&nbsp;2:</p> +<div class="highlight"><pre><span></span><code><span class="p">{</span><span class="o">%</span> <span class="k">for</span> <span class="n">fw</span> <span class="ow">in</span> <span class="n">frameworks</span> <span class="o">%</span><span class="p">}</span> + <span class="p">{</span><span class="o">%</span> <span class="k">if</span> <span class="n">fw</span><span class="o">|</span><span class="n">length</span> <span class="o">&gt;</span> <span class="mi">5</span> <span class="o">%</span><span class="p">}</span> + <span class="o">&lt;</span><span class="n">b</span><span class="o">&gt;</span><span class="p">{{</span> <span class="n">fw</span> <span class="p">}}</span><span class="o">&lt;/</span><span class="n">b</span><span class="o">&gt;</span> + <span class="p">{</span><span class="o">%</span> <span class="k">else</span> <span class="o">%</span><span class="p">}</span> + <span class="p">{{</span> <span class="n">fw</span> <span class="p">}}</span> + <span class="p">{</span><span class="o">%</span> <span class="n">endif</span> <span class="o">%</span><span class="p">}</span> +<span class="p">{</span><span class="o">%</span> <span class="n">endfor</span> <span class="o">%</span><span class="p">}</span> +</code></pre></div> + +<p>nhưng sự khác biệt bắt đầu xuất hiện khi sử dụng&nbsp;method.</p> +<h3>Gọi method trong Django&nbsp;template</h3> +<p>Jinja2 sử dụng cú pháp gọi method như Python, kết quả không có gì ngạc nhiên khi gọi <code>c.items()</code>:</p> +<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">jinja2</span> +<span class="kn">from</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="n">Counter</span> + +<span class="n">c</span> <span class="o">=</span> <span class="n">Counter</span><span class="p">(</span><span class="s2">&quot;meo meo go&quot;</span><span class="o">.</span><span class="n">split</span><span class="p">())</span> +<span class="nb">print</span><span class="p">(</span><span class="n">jinja2</span><span class="o">.</span><span class="n">Template</span><span class="p">(</span><span class="s1">&#39;{{ c.items }}&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">render</span><span class="p">(</span><span class="n">c</span><span class="o">=</span><span class="n">c</span><span class="p">))</span> +<span class="c1"># &lt;built-in method items of Counter object at 0x7fbe37ce64b0&gt;</span> +<span class="nb">print</span><span class="p">(</span><span class="n">jinja2</span><span class="o">.</span><span class="n">Template</span><span class="p">(</span><span class="s1">&#39;{</span><span class="si">% f</span><span class="s1">or k, v in c.items() %} {{ k }} {{ v }} {</span><span class="si">%e</span><span class="s1">ndfor%}&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">render</span><span class="p">(</span><span class="n">c</span><span class="o">=</span><span class="n">c</span><span class="p">))</span> +<span class="c1"># meo 2 go 1</span> +</code></pre></div> + +<p>Django template không dùng cú pháp gọi&nbsp;method</p> +<div class="highlight"><pre><span></span><code><span class="p">{{</span> <span class="n">counter</span><span class="o">.</span><span class="n">items</span><span class="p">()</span> <span class="p">}}</span> +</code></pre></div> + +<p>Gặp&nbsp;error:</p> +<div class="highlight"><pre><span></span><code><span class="n">Could</span> <span class="ow">not</span> <span class="n">parse</span> <span class="n">the</span> <span class="n">remainder</span><span class="p">:</span> <span class="s1">&#39;()&#39;</span> <span class="kn">from</span> <span class="s1">&#39;counter.items()&#39;</span> +</code></pre></div> + +<p>Trong tài liệu Django template <a href="https://docs.djangoproject.com/en/4.2/topics/templates/#variables">topic</a>&nbsp;viết:</p> +<blockquote> +<p>Dictionary lookup, attribute lookup and list-index lookups are implemented with a dot notation: + <code>{{ my_dict.key }} + {{ my_object.attribute }} + {{ my_list.0 }}</code> + If a variable resolves to a callable, the template system will call it with no arguments and use its result instead of the&nbsp;callable.</p> +</blockquote> +<p>Khi sử dụng <code>object.key</code>, Django sẽ thử tìm <code>object["key"]</code>, <code>object.key</code> hay nếu key là method, sẽ gọi <code>object.key</code>, +vì vậy sẽ phải viết <code>{{ counter.items }}</code>.</p> +<p>Chỉ đơn giản là không phải viết <code>()</code>, gì mà phức&nbsp;tạp?</p> +<h3>Tìm&nbsp;bug</h3> +<p>Cho 1 đoạn Django template, nhận context là 1 <a href="https://n.pymi.vn/counter.html">Counter</a>:</p> +<p>Code <code>views.py</code></p> +<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">django.shortcuts</span> <span class="kn">import</span> <span class="n">render</span> +<span class="kn">from</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="n">Counter</span> +<span class="k">def</span> <span class="nf">index</span><span class="p">(</span><span class="n">request</span><span class="p">):</span> + <span class="n">counter</span> <span class="o">=</span> <span class="n">Counter</span><span class="p">(</span><span class="s2">&quot;red green green red red green green&quot;</span><span class="o">.</span><span class="n">split</span><span class="p">())</span> + <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">counter</span><span class="o">.</span><span class="n">items</span><span class="p">():</span> + <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;from counter&quot;</span><span class="p">,</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> + <span class="k">return</span> <span class="n">render</span><span class="p">(</span><span class="n">request</span><span class="p">,</span> <span class="s2">&quot;index.html&quot;</span><span class="p">,</span> <span class="p">{</span><span class="s2">&quot;d&quot;</span><span class="p">:</span> <span class="n">d</span><span class="p">,</span> <span class="s2">&quot;counter&quot;</span><span class="p">:</span> <span class="n">counter</span><span class="p">})</span> +</code></pre></div> + +<p>File <code>templates/index.html</code></p> +<div class="highlight"><pre><span></span><code><span class="p">{</span><span class="o">%</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">counter</span><span class="o">.</span><span class="n">items</span> <span class="o">%</span><span class="p">}</span> + <span class="p">{{</span> <span class="n">k</span> <span class="p">}}</span> <span class="p">{{</span> <span class="n">v</span> <span class="p">}}</span><span class="o">&lt;</span><span class="n">br</span><span class="o">&gt;</span> +<span class="p">{</span><span class="o">%</span> <span class="n">endfor</span> <span class="o">%</span><span class="p">}</span> +</code></pre></div> + +<p>Xảy ra exception tại dòng&nbsp;for:</p> +<div class="highlight"><pre><span></span><code><span class="ne">TypeError</span> <span class="n">at</span> <span class="o">/</span> + +<span class="s1">&#39;int&#39;</span> <span class="nb">object</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">iterable</span> +</code></pre></div> + +<p>có nghĩa <code>counter.items</code> trả về một <code>int</code> chứ không phải tuple (key, value) như <code>dict.items</code> vẫn trả về. +Khó hiểu hơn nữa, khi phần code tương tự trong function <code>index</code> vẫn chạy bình thường, tại sao <code>counter.items</code> trong Django template trả về&nbsp;int?</p> +<p>May mắn thay, tìm <code>items()</code> trong tài liệu <a href="https://docs.djangoproject.com/en/4.2/ref/templates/language/">reference</a> của Django Template&nbsp;viết:</p> +<p><a href="https://docs.djangoproject.com/en/4.2/ref/templates/language/#variables">https://docs.djangoproject.com/en/4.2/ref/templates/language/#variables</a></p> +<blockquote> +<p>Technically, when the template system encounters a dot, it tries the following lookups, in this order: + Dictionary lookup + Attribute or method lookup + Numeric index lookup +If the resulting value is callable, it is called with no arguments. The result of the call becomes the template&nbsp;value.</p> +</blockquote> +<p>Cũng giống phần đã viết trong <a href="https://docs.djangoproject.com/en/4.2/topics/templates/#variables">topic</a>, nhưng nhấn mạnh hơn về thứ tự&nbsp;thử:</p> +<ul> +<li>tìm kiếm trong dict trước: <code>object[key]</code></li> +<li>truy cập attribute, method cũng là 1 loại attribute: <code>object.key</code></li> +</ul> +<p>Trở lại ví dụ, <code>counter.items</code> đầu tiên sẽ gọi <code>counter[items]</code>, +mà <a href="https://n.pymi.vn/counter.html">Counter trả về 0 với item không tồn tại thay vì raise KeyError</a>, nên kết quả là <code>0</code>, +không có lần gọi nào tới method <code>counter.items()</code>.</p> +<blockquote> +<p>This lookup order can cause some unexpected behavior with objects that override dictionary lookup. For example, consider the following code snippet that attempts to loop over a&nbsp;collections.defaultdict:</p> +<p _="%" endfor>{% for k, v in defaultdict.items %} + Do something with k and v&nbsp;here&#8230;</p> +<p>Because dictionary lookup happens first, that behavior kicks in and provides a default value instead of using the intended .items() method. In this case, consider converting to a dictionary&nbsp;first</p> +</blockquote> +<p>Giải pháp là biến Counter thành dict <code>dict(c)</code> trước khi đưa cho&nbsp;template.</p> +<p>Thành dict là xong??? cho đến 1 ngày đen đủi, khi dict của bạn chứa 1 key là <code>items</code>&#8230;</p> +<p>Xem code tại <a href="https://github.com/django/django/blob/4.2/django/template/base.py#L867-L942">https://github.com/django/django/blob/4.2/django/template/base.py#L867-L942</a></p> +<h3>Kết&nbsp;luận</h3> +<p>Magic không tự xảy ra, muốn dùng magic nhớ đọc doc, hãy thử, đừng&nbsp;đoán.</p> +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>collections.Counter có phải là dict?2023-07-14T00:00:00+07:002023-07-14T00:00:00+07:00Pymier0tag:n.pymi.vn,2023-07-14:/counter.html<p>Lập trình viên Python không bao giờ phải tự đếm số lần xuất hiện của mỗi phần tử, bởi có sẵn&nbsp;Counter:</p> +<h3>Python Counter là&nbsp;gì</h3> +<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="n">Counter</span> +<span class="n">c</span> <span class="o">=</span> <span class="n">Counter</span><span class="p">(</span><span class="s2">&quot;py py thon thon py&quot;</span><span class="o">.</span><span class="n">split</span><span class="p">())</span> +<span class="nb">print</span><span class="p">(</span><span class="n">c</span><span class="p">)</span> +<span class="c1"># Counter({&#39;py&#39;: 3, &#39;thon&#39;: 2})</span> +<span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">c …</span></code></pre></div><p>Lập trình viên Python không bao giờ phải tự đếm số lần xuất hiện của mỗi phần tử, bởi có sẵn&nbsp;Counter:</p> +<h3>Python Counter là&nbsp;gì</h3> +<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="n">Counter</span> +<span class="n">c</span> <span class="o">=</span> <span class="n">Counter</span><span class="p">(</span><span class="s2">&quot;py py thon thon py&quot;</span><span class="o">.</span><span class="n">split</span><span class="p">())</span> +<span class="nb">print</span><span class="p">(</span><span class="n">c</span><span class="p">)</span> +<span class="c1"># Counter({&#39;py&#39;: 3, &#39;thon&#39;: 2})</span> +<span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">c</span><span class="o">.</span><span class="n">items</span><span class="p">():</span> + <span class="nb">print</span><span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span> +<span class="c1"># py 3</span> +<span class="c1"># thon 2</span> +</code></pre></div> + +<p>Thay vì tự&nbsp;viết:</p> +<div class="highlight"><pre><span></span><code><span class="n">d</span> <span class="o">=</span> <span class="p">{}</span> +<span class="k">for</span> <span class="n">w</span> <span class="ow">in</span> <span class="s2">&quot;py py thon thon py&quot;</span><span class="o">.</span><span class="n">split</span><span class="p">():</span> + <span class="k">if</span> <span class="n">w</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">d</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="o">=</span> <span class="mi">1</span> + <span class="k">else</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="o">+=</span> <span class="mi">1</span> + +<span class="nb">print</span><span class="p">(</span><span class="n">d</span><span class="p">)</span> +<span class="c1"># {&#39;py&#39;: 3, &#39;thon&#39;: 2}</span> +</code></pre></div> + +<h3>Counter có phải là&nbsp;dict?</h3> +<p>Trước tiên, cần làm rõ, &#8220;là dict&#8221; có nghĩa là&nbsp;gì?</p> +<ul> +<li>Là kế thừa từ&nbsp;dict?</li> +<li>Là mọi method giống dict? và có thêm vài method&nbsp;khác</li> +</ul> +<p>Dễ thấy, Counter kế thừa từ dict, nên 1 Counter, theo nghĩa của lập trình hướng đối tượng - <span class="caps">OOP</span>, là 1&nbsp;dict:</p> +<div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">Counter</span><span class="p">(</span><span class="nb">dict</span><span class="p">):</span> +<span class="o">...</span> + +<span class="o">&gt;&gt;&gt;</span> <span class="n">c</span> <span class="o">=</span> <span class="n">collections</span><span class="o">.</span><span class="n">Counter</span><span class="p">()</span> +<span class="o">&gt;&gt;&gt;</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">c</span><span class="p">,</span> <span class="nb">dict</span><span class="p">)</span> +<span class="kc">True</span> +</code></pre></div> + +<p>Nếu một người tin vào <strong>THUYẾT</strong> tiến hóa, thì 1 người có phải 1 con vượn? hay 1 con bò sát? hay 1 con sinh vật đơn bào? Theo <span class="caps">OOP</span> thì là&nbsp;vậy.</p> +<p>Đó là lỗ hổng trong cách tư duy <span class="caps">OOP</span>, những ngôn ngữ lập trình mới ngày nay không theo lối suy nghĩ này, mà dựa trên khả năng của 1 object để xét nó là gì như Go interface hay Rust trait: một object là <code>Writer</code> nếu nó có thể <code>write</code>, là <code>Reader</code> nếu nó có thể <code>read</code>.</p> +<p>Quay trờ lại câu trả lời: Counter là 1 dict, câu trả lời này có tác dụng gì? có thể dùng Counter như dict mà không có khác biệt gì? +Đôi khi không&nbsp;phải:</p> +<p>dict</p> +<div class="highlight"><pre><span></span><code><span class="nb">print</span><span class="p">(</span><span class="n">d</span><span class="p">[</span><span class="s1">&#39;py&#39;</span><span class="p">])</span> +<span class="c1"># 3</span> +<span class="nb">print</span><span class="p">(</span><span class="n">d</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;secret&#39;</span><span class="p">))</span> +<span class="c1"># None</span> +<span class="nb">print</span><span class="p">(</span><span class="n">d</span><span class="p">[</span><span class="s1">&#39;secret&#39;</span><span class="p">])</span> +<span class="c1"># Traceback (most recent call last):</span> +<span class="c1"># File &quot;&lt;stdin&gt;&quot;, line 1, in &lt;module&gt;</span> +<span class="c1"># KeyError: &#39;secret&#39;</span> +</code></pre></div> + +<p>Counter thì&nbsp;khác</p> +<div class="highlight"><pre><span></span><code><span class="nb">print</span><span class="p">(</span><span class="n">c</span><span class="p">[</span><span class="s1">&#39;py&#39;</span><span class="p">])</span> +<span class="c1"># 3</span> +<span class="nb">print</span><span class="p">(</span><span class="n">c</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s1">&#39;recret&#39;</span><span class="p">))</span> +<span class="c1"># None</span> +<span class="nb">print</span><span class="p">(</span><span class="n">c</span><span class="p">[</span><span class="s1">&#39;secret&#39;</span><span class="p">])</span> +<span class="c1"># 0</span> +</code></pre></div> + +<p>Truy cập 1 key không tồn tại trong Counter trả về 0, <a href="https://docs.python.org/3.10/reference/datamodel.html?highlight=__missing__#object.__missing__">method <code>__missing__</code></a> quyết định điều&nbsp;này:</p> +<div class="highlight"><pre><span></span><code><span class="nb">object</span><span class="o">.</span><span class="fm">__missing__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">)</span> + + <span class="n">Called</span> <span class="n">by</span> <span class="nb">dict</span><span class="o">.</span><span class="fm">__getitem__</span><span class="p">()</span> <span class="n">to</span> <span class="n">implement</span> <span class="bp">self</span><span class="p">[</span><span class="n">key</span><span class="p">]</span> <span class="k">for</span> <span class="nb">dict</span> <span class="n">subclasses</span> + <span class="n">when</span> <span class="n">key</span> <span class="ow">is</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">the</span> <span class="n">dictionary</span><span class="o">.</span> +</code></pre></div> + +<p>Code Counter <a href="https://github.com/python/cpython/blob/v3.11.0/Lib/collections/__init__.py#L599C1-L602C17">https://github.com/python/cpython/blob/v3.11.0/Lib/collections/__init__.py#<span class="caps">L599C1</span>-<span class="caps">L602C17</span></a>:</p> +<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="fm">__missing__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">key</span><span class="p">):</span> + <span class="s1">&#39;The count of elements not in the Counter is zero.&#39;</span> + <span class="c1"># Needed so that self[missing_item] does not raise KeyError</span> + <span class="k">return</span> <span class="mi">0</span> +</code></pre></div> + +<p>Giờ thì cách hoạt động của Counter đã khác hoàn toàn dict, vậy counter có phải&nbsp;dict?</p> +<h3>Kết&nbsp;luận</h3> +<p>Nói đúng cũng phải, nói không đúng cũng phải, chủ yếu là người nghe muốn nghe cái&nbsp;gì.</p> +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>dict.get hay or?2023-07-12T00:00:00+07:002023-07-12T00:00:00+07:00Pymier0tag:n.pymi.vn,2023-07-12:/dict.get.html<p><code>dict.get(k, default)</code> có bằng <code>dict.get(k) or default</code>?</p> +<p><code>or</code> thường được dùng để trả về giá trị mặc&nbsp;định:</p> +<div class="highlight"><pre><span></span><code>host = os.environ.get(&quot;host&quot;) or &quot;localhost&quot; +</code></pre></div> + +<p>Nhưng:</p> +<p><code>or</code> có cách hoạt động khá lắt&nbsp;léo:</p> +<p>các giá trị được biến thành boolean trước khi xử lý, với …</p><p><code>dict.get(k, default)</code> có bằng <code>dict.get(k) or default</code>?</p> +<p><code>or</code> thường được dùng để trả về giá trị mặc&nbsp;định:</p> +<div class="highlight"><pre><span></span><code>host = os.environ.get(&quot;host&quot;) or &quot;localhost&quot; +</code></pre></div> + +<p>Nhưng:</p> +<p><code>or</code> có cách hoạt động khá lắt&nbsp;léo:</p> +<p>các giá trị được biến thành boolean trước khi xử lý, với giá trị trong dict là +0, &#8220;&#8221;, [],&#8230; nó lại bị <code>or</code> coi là không có gì, và lấy vế phải ra&nbsp;dùng.</p> +<p>Nên nếu dict là&nbsp;:</p> +<div class="highlight"><pre><span></span><code><span class="n">d</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&quot;red&quot;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="s2">&quot;yellow&quot;</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="s2">&quot;green&quot;</span><span class="p">:</span> <span class="mi">2</span><span class="p">}</span> +</code></pre></div> + +<p>thì kết quả khác&nbsp;nhau:</p> +<div class="highlight"><pre><span></span><code><span class="n">d</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;red&quot;</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span> +</code></pre></div> + +<p>còn</p> +<div class="highlight"><pre><span></span><code><span class="n">d</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;red&quot;</span><span class="p">)</span> <span class="ow">or</span> <span class="mi">3</span> <span class="o">==</span> <span class="mi">3</span> +</code></pre></div> + +<h3>Kết&nbsp;luận</h3> +<div class="highlight"><pre><span></span><code><span class="n">get</span><span class="p">(</span><span class="n">key</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="o">/</span><span class="p">)</span> <span class="n">method</span> <span class="n">of</span> <span class="n">builtins</span><span class="o">.</span><span class="n">dict</span> <span class="n">instance</span> + <span class="n">Return</span> <span class="n">the</span> <span class="n">value</span> <span class="k">for</span> <span class="n">key</span> <span class="k">if</span> <span class="n">key</span> <span class="ow">is</span> <span class="ow">in</span> <span class="n">the</span> <span class="n">dictionary</span><span class="p">,</span> <span class="k">else</span> <span class="n">default</span><span class="o">.</span> +</code></pre></div> + +<p>Đôi khi nhìn qua thì giống, nhưng phải sờ tận tay mới biết khác chỗ&nbsp;nào.</p> +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>__new__ và __init__2023-05-28T00:00:00+07:002023-05-28T00:00:00+07:00Pymier0tag:n.pymi.vn,2023-05-28:/new.html<p><center> +<img alt="new" src="https://images.unsplash.com/photo-1620924049153-4d32fcbe88fe?ixlib=rb-4.0.3&amp;q=85&amp;fm=jpg&amp;crop=entropy&amp;cs=srgb&amp;dl=nick-fewings-1SsUquHPNT8-unsplash.jpg&amp;w=640"> +Photo by <a href="https://unsplash.com/@jannerboy62?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Nick Fewings</a> on <a href="https://unsplash.com/photos/1SsUquHPNT8?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a> +</center></p> +<h3><code>__new__</code> và <code>__init__</code> khác&nbsp;gì?</h3> +<p>Như đa phần các câu hỏi phỏng vấn nặng tính lý thuyết, ít ứng dụng thực tế hay trăm năm dùng 1 lần, đây không là ngoại&nbsp;lệ.</p> +<p>Các câu khác tương&nbsp;tự:</p> +<ul> +<li>decorator là gì (<strong>CHƯA</strong> bao giờ …</li></ul><p><center> +<img alt="new" src="https://images.unsplash.com/photo-1620924049153-4d32fcbe88fe?ixlib=rb-4.0.3&amp;q=85&amp;fm=jpg&amp;crop=entropy&amp;cs=srgb&amp;dl=nick-fewings-1SsUquHPNT8-unsplash.jpg&amp;w=640"> +Photo by <a href="https://unsplash.com/@jannerboy62?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Nick Fewings</a> on <a href="https://unsplash.com/photos/1SsUquHPNT8?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a> +</center></p> +<h3><code>__new__</code> và <code>__init__</code> khác&nbsp;gì?</h3> +<p>Như đa phần các câu hỏi phỏng vấn nặng tính lý thuyết, ít ứng dụng thực tế hay trăm năm dùng 1 lần, đây không là ngoại&nbsp;lệ.</p> +<p>Các câu khác tương&nbsp;tự:</p> +<ul> +<li>decorator là gì (<strong>CHƯA</strong> bao giờ thấy ai hỏi generator dù generator dùng nhiều gấp decorator trăm&nbsp;lần).</li> +<li><span class="caps">GIL</span> là gì, thread với process khác gì nhau, nhược điểm của thread trong Python. (Không thực sự vô dụng, nhưng hiếm dùng với web-developer) Xem câu trả lời ở <a href="https://docs.python.org/3/library/threading.html">https://docs.python.org/3/library/threading.html</a></li> +</ul> +<h3>Python <code>__init__</code></h3> +<p><code>__init__</code> thì ai viết class cũng dùng, method <code>__init__</code> dùng để khởi tạo một object (class instance), ví&nbsp;dụ:</p> +<div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">Cat</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">name</span><span class="p">,</span> <span class="n">age</span><span class="p">):</span> + <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Calling __init__&quot;</span><span class="p">)</span> + <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span> + <span class="bp">self</span><span class="o">.</span><span class="n">age</span> <span class="o">=</span> <span class="n">age</span> +</code></pre></div> + +<h3>Python <code>__new__</code> có hay dùng&nbsp;không?</h3> +<p><code>__new__</code> rất ít khi thấy, vì rất ít được dùng. Thử tìm trong 3 web framework phổ biến nhất của Python: Django, Flask, FastAPI,&nbsp;thấy:</p> +<div class="highlight"><pre><span></span><code><span class="err">$</span> <span class="n">grep</span> <span class="o">-</span><span class="n">Rin</span> <span class="s1">&#39;def __new__&#39;</span> <span class="n">django</span> +<span class="n">django</span><span class="o">/</span><span class="n">tests</span><span class="o">/</span><span class="n">model_forms</span><span class="o">/</span><span class="n">tests</span><span class="o">.</span><span class="n">py</span><span class="p">:</span><span class="mi">3615</span><span class="p">:</span> <span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="n">attrs</span><span class="p">):</span> +<span class="n">django</span><span class="o">/</span><span class="n">django</span><span class="o">/</span><span class="n">utils</span><span class="o">/</span><span class="n">datastructures</span><span class="o">.</span><span class="n">py</span><span class="p">:</span><span class="mi">233</span><span class="p">:</span> <span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="n">warning</span><span class="o">=</span><span class="s2">&quot;ImmutableList object is immutable.&quot;</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span> +<span class="n">django</span><span class="o">/</span><span class="n">django</span><span class="o">/</span><span class="n">utils</span><span class="o">/</span><span class="n">deconstruct</span><span class="o">.</span><span class="n">py</span><span class="p">:</span><span class="mi">15</span><span class="p">:</span> <span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</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">kwargs</span><span class="p">):</span> +<span class="n">django</span><span class="o">/</span><span class="n">django</span><span class="o">/</span><span class="n">utils</span><span class="o">/</span><span class="n">deprecation</span><span class="o">.</span><span class="n">py</span><span class="p">:</span><span class="mi">55</span><span class="p">:</span> <span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="n">attrs</span><span class="p">):</span> +<span class="n">django</span><span class="o">/</span><span class="n">django</span><span class="o">/</span><span class="n">test</span><span class="o">/</span><span class="n">selenium</span><span class="o">.</span><span class="n">py</span><span class="p">:</span><span class="mi">23</span><span class="p">:</span> <span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="n">attrs</span><span class="p">):</span> +<span class="n">django</span><span class="o">/</span><span class="n">django</span><span class="o">/</span><span class="n">forms</span><span class="o">/</span><span class="n">widgets</span><span class="o">.</span><span class="n">py</span><span class="p">:</span><span class="mi">216</span><span class="p">:</span> <span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="n">mcs</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="n">attrs</span><span class="p">):</span> +<span class="n">django</span><span class="o">/</span><span class="n">django</span><span class="o">/</span><span class="n">forms</span><span class="o">/</span><span class="n">models</span><span class="o">.</span><span class="n">py</span><span class="p">:</span><span class="mi">269</span><span class="p">:</span> <span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="n">mcs</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="n">attrs</span><span class="p">):</span> +<span class="n">django</span><span class="o">/</span><span class="n">django</span><span class="o">/</span><span class="n">forms</span><span class="o">/</span><span class="n">forms</span><span class="o">.</span><span class="n">py</span><span class="p">:</span><span class="mi">24</span><span class="p">:</span> <span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="n">mcs</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="n">attrs</span><span class="p">):</span> +<span class="n">django</span><span class="o">/</span><span class="n">django</span><span class="o">/</span><span class="n">db</span><span class="o">/</span><span class="n">migrations</span><span class="o">/</span><span class="n">operations</span><span class="o">/</span><span class="n">base</span><span class="o">.</span><span class="n">py</span><span class="p">:</span><span class="mi">36</span><span class="p">:</span> <span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</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">kwargs</span><span class="p">):</span> +<span class="n">django</span><span class="o">/</span><span class="n">django</span><span class="o">/</span><span class="n">db</span><span class="o">/</span><span class="n">migrations</span><span class="o">/</span><span class="n">migration</span><span class="o">.</span><span class="n">py</span><span class="p">:</span><span class="mi">231</span><span class="p">:</span> <span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">value</span><span class="p">,</span> <span class="n">setting</span><span class="p">):</span> +<span class="n">django</span><span class="o">/</span><span class="n">django</span><span class="o">/</span><span class="n">db</span><span class="o">/</span><span class="n">models</span><span class="o">/</span><span class="n">manager</span><span class="o">.</span><span class="n">py</span><span class="p">:</span><span class="mi">21</span><span class="p">:</span> <span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</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">kwargs</span><span class="p">):</span> +<span class="n">django</span><span class="o">/</span><span class="n">django</span><span class="o">/</span><span class="n">db</span><span class="o">/</span><span class="n">models</span><span class="o">/</span><span class="n">enums</span><span class="o">.</span><span class="n">py</span><span class="p">:</span><span class="mi">12</span><span class="p">:</span> <span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="n">metacls</span><span class="p">,</span> <span class="n">classname</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="n">classdict</span><span class="p">,</span> <span class="o">**</span><span class="n">kwds</span><span class="p">):</span> +<span class="n">django</span><span class="o">/</span><span class="n">django</span><span class="o">/</span><span class="n">db</span><span class="o">/</span><span class="n">models</span><span class="o">/</span><span class="n">base</span><span class="o">.</span><span class="n">py</span><span class="p">:</span><span class="mi">95</span><span class="p">:</span> <span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="n">attrs</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span> +<span class="n">django</span><span class="o">/</span><span class="n">django</span><span class="o">/</span><span class="n">conf</span><span class="o">/</span><span class="fm">__init__</span><span class="o">.</span><span class="n">py</span><span class="p">:</span><span class="mi">41</span><span class="p">:</span> <span class="k">def</span> <span class="fm">__new__</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">setting_name</span><span class="p">):</span> +<span class="err">$</span> <span class="n">grep</span> <span class="o">-</span><span class="n">Rin</span> <span class="s1">&#39;def __new__&#39;</span> <span class="n">flask</span> +<span class="err">$</span> <span class="n">grep</span> <span class="o">-</span><span class="n">Rin</span> <span class="s1">&#39;def __new__&#39;</span> <span class="n">fastapi</span> +<span class="err">$</span> <span class="n">grep</span> <span class="o">-</span><span class="n">Rin</span> <span class="s1">&#39;def __new__&#39;</span> <span class="n">werkzeug</span> +<span class="n">werkzeug</span><span class="o">/</span><span class="n">src</span><span class="o">/</span><span class="n">werkzeug</span><span class="o">/</span><span class="n">urls</span><span class="o">.</span><span class="n">py</span><span class="p">:</span><span class="mi">71</span><span class="p">:</span> <span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Any</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Any</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">BaseURL</span><span class="p">:</span> +<span class="n">werkzeug</span><span class="o">/</span><span class="n">examples</span><span class="o">/</span><span class="n">cupoftee</span><span class="o">/</span><span class="n">application</span><span class="o">.</span><span class="n">py</span><span class="p">:</span><span class="mi">49</span><span class="p">:</span> <span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="n">metacls</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">this_bases</span><span class="p">,</span> <span class="n">d</span><span class="p">):</span> +</code></pre></div> + +<p>Chỉ mình <code>django</code> có viết <code>__new__</code>, 2 framework còn lại không hề thấy, tìm thêm <code>werkzeug</code> (của Flask), thấy 1 ví dụ không quá hữu dụng (để hiện warning deprecate method)&nbsp;werkzeug/src/werkzeug/urls.py:</p> +<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Any</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">t</span><span class="o">.</span><span class="n">Any</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">BaseURL</span><span class="p">:</span> + <span class="n">warnings</span><span class="o">.</span><span class="n">warn</span><span class="p">(</span> + <span class="sa">f</span><span class="s2">&quot;&#39;werkzeug.urls.</span><span class="si">{</span><span class="bp">cls</span><span class="o">.</span><span class="vm">__name__</span><span class="si">}</span><span class="s2">&#39; is deprecated and will be removed in&quot;</span> + <span class="s2">&quot; Werkzeug 3.0. Use the &#39;urllib.parse&#39; library instead.&quot;</span><span class="p">,</span> + <span class="ne">DeprecationWarning</span><span class="p">,</span> + <span class="n">stacklevel</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> + <span class="p">)</span> + <span class="k">return</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</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">kwargs</span><span class="p">)</span> +</code></pre></div> + +<blockquote> +<p><code>__new__()</code> is intended mainly to allow subclasses of immutable types (like int, str, or tuple) to customize instance creation. It is also commonly overridden in custom metaclasses in order to customize class&nbsp;creation.</p> +</blockquote> +<p>Django subclass&nbsp;str:</p> +<div class="highlight"><pre><span></span><code><span class="c1"># django/conf/__init__.py</span> +<span class="k">class</span> <span class="nc">SettingsReference</span><span class="p">(</span><span class="nb">str</span><span class="p">):</span> + <span class="sd">&quot;&quot;&quot;</span> +<span class="sd"> String subclass which references a current settings value. It&#39;s treated as</span> +<span class="sd"> the value in memory but serializes to a settings.NAME attribute reference.</span> +<span class="sd"> &quot;&quot;&quot;</span> + + <span class="k">def</span> <span class="fm">__new__</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">setting_name</span><span class="p">):</span> + <span class="k">return</span> <span class="nb">str</span><span class="o">.</span><span class="fm">__new__</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="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">setting_name</span><span class="p">):</span> + <span class="bp">self</span><span class="o">.</span><span class="n">setting_name</span> <span class="o">=</span> <span class="n">setting_name</span> +</code></pre></div> + +<h3>Python <code>__new__</code> là&nbsp;gì</h3> +<p><code>__new__</code> là một staticmethod (là một trường hợp đặc biệt, nên không cần @staticmethod), dùng để tạo 1 instance của class (tạo object). Sau khi tạo ra object rồi, object gọi <code>__init__</code> để &#8220;initialize&#8221; (khởi tạo các giá&nbsp;trị).</p> +<div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">Cat</span><span class="p">():</span> + <span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</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">kwarsg</span><span class="p">):</span> + <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Calling __new__&quot;</span><span class="p">)</span> + <span class="k">return</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</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">name</span><span class="p">,</span> <span class="n">age</span><span class="p">):</span> + <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Calling __init__&quot;</span><span class="p">)</span> + <span class="bp">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">name</span> + <span class="bp">self</span><span class="o">.</span><span class="n">age</span> <span class="o">=</span> <span class="n">age</span> + +<span class="n">c</span> <span class="o">=</span> <span class="n">Cat</span><span class="p">(</span><span class="s2">&quot;Meo&quot;</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> +</code></pre></div> + +<div class="highlight"><pre><span></span><code>Calling __new__ +Calling __init__ +</code></pre></div> + +<blockquote> +<p>Because <code>__new__()</code> and <code>__init__()</code> work together in constructing objects (<code>__new__()</code> to create it, and <code>__init__()</code> to customize it), no non-None value may be returned by <code>__init__()</code></p> +</blockquote> +<p><code>__new__</code> thực hiện việc tạo object, nên có thể đặt các logic đặc biệt về tạo object ở đây, ví dụ chỉ cho phép tạo N object, hay chỉ cho phép tạo 1 object duy nhất (còn gọi là singleton pattern). Ví&nbsp;dụ:</p> +<ul> +<li><a href="https://docs.python.org/3/faq/programming.html?highlight=__new__#how-can-a-subclass-control-what-data-is-stored-in-an-immutable-instance">https://docs.python.org/3/faq/programming.html?highlight=__new__#how-can-a-subclass-control-what-data-is-stored-in-an-immutable-instance</a></li> +<li><a href="https://stackoverflow.com/a/8106977">https://stackoverflow.com/a/8106977</a></li> +<li><a href="https://stackoverflow.com/a/8665179">https://stackoverflow.com/a/8665179</a></li> +</ul> +<h3>Kết&nbsp;luận</h3> +<ul> +<li><code>__new__</code> là static method (thuộc về class), <code>__init__</code> là method (thuộc về&nbsp;instance/object)</li> +<li><code>__new__</code> gọi trước, <code>__init__</code> gọi&nbsp;sau</li> +<li><code>__new__</code> trả về instance của class, <code>__init__</code> trả về&nbsp;None</li> +<li><code>__init__</code> ở mọi nơi, <code>__new__</code> ít được&nbsp;dùng.</li> +</ul> +<p>Code Flask hay FastAPI 5 năm, cũng không 1 lần viết <code>__new__</code>!</p> +<h3>Tham&nbsp;khảo</h3> +<ul> +<li><a href="https://docs.python.org/3/reference/datamodel.html?highlight=__new__#object.__new__">https://docs.python.org/3/reference/datamodel.html?highlight=__new__#object.__new__</a></li> +</ul> +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Bất ngờ với __builtins__2023-05-07T11:00:01+07:002023-05-07T11:00:01+07:00Pymier0tag:n.pymi.vn,2023-05-07:/builtins.html<p>Python có vài chục function có sẵn, liệt kê tại <a href="https://docs.python.org/3/library/functions.html">https://docs.python.org/3/library/functions.html</a>, bật lệnh <code>python3</code> lên gõ là&nbsp;có:</p> +<div class="highlight"><pre><span></span><code><span class="err">$</span> <span class="n">python3</span> +<span class="n">Python</span> <span class="mf">3.10.10</span> <span class="p">(</span><span class="n">main</span><span class="p">,</span> <span class="n">Mar</span> <span class="mi">5</span> <span class="mi">2023</span><span class="p">,</span> <span class="mi">22</span><span class="p">:</span><span class="mi">26</span><span class="p">:</span><span class="mi">53</span><span class="p">)</span> <span class="p">[</span><span class="n">GCC</span> <span class="mf">12.2.1</span> <span class="mi">20230201</span><span class="p">]</span> <span class="n">on</span> <span class="n">linux</span> +<span class="n">Type</span> <span class="s2">&quot;help&quot;</span><span class="p">,</span> <span class="s2">&quot;copyright&quot;</span><span class="p">,</span> <span class="s2">&quot;credits&quot;</span> <span class="ow">or</span> <span class="s2">&quot;license …</span></code></pre></div><p>Python có vài chục function có sẵn, liệt kê tại <a href="https://docs.python.org/3/library/functions.html">https://docs.python.org/3/library/functions.html</a>, bật lệnh <code>python3</code> lên gõ là&nbsp;có:</p> +<div class="highlight"><pre><span></span><code><span class="err">$</span> <span class="n">python3</span> +<span class="n">Python</span> <span class="mf">3.10.10</span> <span class="p">(</span><span class="n">main</span><span class="p">,</span> <span class="n">Mar</span> <span class="mi">5</span> <span class="mi">2023</span><span class="p">,</span> <span class="mi">22</span><span class="p">:</span><span class="mi">26</span><span class="p">:</span><span class="mi">53</span><span class="p">)</span> <span class="p">[</span><span class="n">GCC</span> <span class="mf">12.2.1</span> <span class="mi">20230201</span><span class="p">]</span> <span class="n">on</span> <span class="n">linux</span> +<span class="n">Type</span> <span class="s2">&quot;help&quot;</span><span class="p">,</span> <span class="s2">&quot;copyright&quot;</span><span class="p">,</span> <span class="s2">&quot;credits&quot;</span> <span class="ow">or</span> <span class="s2">&quot;license&quot;</span> <span class="k">for</span> <span class="n">more</span> <span class="n">information</span><span class="o">.</span> +<span class="o">&gt;&gt;&gt;</span> <span class="nb">print</span><span class="p">(</span><span class="nb">len</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="mi">2</span><span class="p">]))</span> +<span class="mi">3</span> +</code></pre></div> + +<p>Định nghĩa 1 function rồi gọi <code>globals</code> để liệt kê tất cả các &#8220;tên&#8221; có thể truy cập&nbsp;được:</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="k">def</span> <span class="nf">double</span><span class="p">(</span><span class="n">x</span><span class="p">):</span> +<span class="o">...</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="o">&gt;&gt;&gt;</span> <span class="n">n</span> <span class="o">=</span> <span class="mi">42</span> +<span class="o">&gt;&gt;&gt;</span> <span class="nb">globals</span><span class="p">()</span> +<span class="p">{</span><span class="s1">&#39;__name__&#39;</span><span class="p">:</span> <span class="s1">&#39;__main__&#39;</span><span class="p">,</span> <span class="s1">&#39;__doc__&#39;</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span> <span class="s1">&#39;__package__&#39;</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span> <span class="s1">&#39;__loader__&#39;</span><span class="p">:</span> <span class="o">&lt;</span><span class="k">class</span> <span class="err">&#39;</span><span class="nc">_frozen_importlib</span><span class="o">.</span><span class="n">BuiltinImporter</span><span class="s1">&#39;&gt;, &#39;</span><span class="n">__spec__</span><span class="s1">&#39;: None, &#39;</span><span class="vm">__annotations__</span><span class="s1">&#39;: </span><span class="si">{}</span><span class="s1">, &#39;</span><span class="n">__builtins__</span><span class="s1">&#39;: &lt;module &#39;</span><span class="n">builtins</span><span class="s1">&#39; (built-in)&gt;, &#39;</span><span class="n">double</span><span class="s1">&#39;: &lt;function double at 0x7f729f6065f0&gt;, &#39;</span><span class="n">n</span><span class="s1">&#39;: 42}</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">help</span><span class="p">(</span><span class="nb">globals</span><span class="p">)</span> +<span class="nb">globals</span><span class="p">()</span> + <span class="n">Return</span> <span class="n">the</span> <span class="n">dictionary</span> <span class="n">containing</span> <span class="n">the</span> <span class="n">current</span> <span class="n">scope</span><span class="s1">&#39;s global variables.</span> + + <span class="n">NOTE</span><span class="p">:</span> <span class="n">Updates</span> <span class="n">to</span> <span class="n">this</span> <span class="n">dictionary</span> <span class="o">*</span><span class="n">will</span><span class="o">*</span> <span class="n">affect</span> <span class="n">name</span> <span class="n">lookups</span> <span class="ow">in</span> <span class="n">the</span> <span class="n">current</span> + <span class="k">global</span> <span class="n">scope</span> <span class="ow">and</span> <span class="n">vice</span><span class="o">-</span><span class="n">versa</span><span class="o">.</span> +</code></pre></div> + +<p>Python interpreter interative mode có hỗ trợ &#8220;autocomplete&#8221;, gõ <code>__</code> rồi bấm phím Tab,&nbsp;thấy:</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="n">__</span> +<span class="vm">__annotations__</span> <span class="vm">__doc__</span> <span class="vm">__name__</span> +<span class="n">__build_class__</span><span class="p">(</span> <span class="nb">__import__</span><span class="p">(</span> <span class="n">__package__</span> +<span class="n">__debug__</span> <span class="n">__loader__</span><span class="p">()</span> <span class="n">__spec__</span> +</code></pre></div> + +<p>Thấy có chút khác biệt: autocomplete không hiện <code>__builtins__</code>, còn <code>globals()</code> lại không có <code>__debug__</code> và <code>__import__</code>, <code>__build_class__</code>.</p> +<h3><code>__import__</code></h3> +<div class="highlight"><pre><span></span><code><span class="nb">__import__</span><span class="p">(</span><span class="o">...</span><span class="p">)</span> + <span class="nb">__import__</span><span class="p">(</span><span class="n">name</span><span class="p">,</span> <span class="nb">globals</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="nb">locals</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">fromlist</span><span class="o">=</span><span class="p">(),</span> <span class="n">level</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">module</span> + + <span class="n">Import</span> <span class="n">a</span> <span class="n">module</span><span class="o">.</span> <span class="n">Because</span> <span class="n">this</span> <span class="n">function</span> <span class="ow">is</span> <span class="n">meant</span> <span class="k">for</span> <span class="n">use</span> <span class="n">by</span> <span class="n">the</span> <span class="n">Python</span> + <span class="n">interpreter</span> <span class="ow">and</span> <span class="ow">not</span> <span class="k">for</span> <span class="n">general</span> <span class="n">use</span><span class="p">,</span> <span class="n">it</span> <span class="ow">is</span> <span class="n">better</span> <span class="n">to</span> <span class="n">use</span> + <span class="n">importlib</span><span class="o">.</span><span class="n">import_module</span><span class="p">()</span> <span class="n">to</span> <span class="n">programmatically</span> <span class="kn">import</span> <span class="nn">a</span> <span class="n">module</span><span class="o">.</span> + <span class="o">...</span> +</code></pre></div> + +<p>Function <code>__import__</code> thực hiện import khi gõ <code>import lib</code>.</p> +<h3><code>__builtins__</code></h3> +<p><code>__builtins__</code> là module <code>builtins</code></p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">builtins</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">builtins</span><span class="o">.</span><span class="n">print</span><span class="p">(</span><span class="n">builtins</span><span class="o">.</span><span class="n">len</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="o">&gt;&gt;&gt;</span> <span class="n">help</span><span class="p">(</span><span class="n">__builtins__</span><span class="p">)</span> +<span class="n">Help</span> <span class="n">on</span> <span class="n">built</span><span class="o">-</span><span class="ow">in</span> <span class="n">module</span> <span class="n">builtins</span><span class="p">:</span> + +<span class="n">NAME</span> + <span class="n">builtins</span> <span class="o">-</span> <span class="n">Built</span><span class="o">-</span><span class="ow">in</span> <span class="n">functions</span><span class="p">,</span> <span class="n">exceptions</span><span class="p">,</span> <span class="ow">and</span> <span class="n">other</span> <span class="n">objects</span><span class="o">.</span> + <span class="o">...</span> +</code></pre></div> + +<p>nơi chứa các builtin functions như print, len,&nbsp;&#8230;</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="p">[</span><span class="n">i</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">dir</span><span class="p">(</span><span class="n">__builtins__</span><span class="p">)</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">i</span><span class="o">.</span><span class="n">startswith</span><span class="p">(</span><span class="s2">&quot;__&quot;</span><span class="p">)</span> <span class="ow">and</span> <span class="n">i</span><span class="o">.</span><span class="n">islower</span><span class="p">()]</span> +<span class="p">[</span><span class="s1">&#39;abs&#39;</span><span class="p">,</span> <span class="s1">&#39;aiter&#39;</span><span class="p">,</span> <span class="s1">&#39;all&#39;</span><span class="p">,</span> <span class="s1">&#39;anext&#39;</span><span class="p">,</span> <span class="s1">&#39;any&#39;</span><span class="p">,</span> <span class="s1">&#39;ascii&#39;</span><span class="p">,</span> <span class="s1">&#39;bin&#39;</span><span class="p">,</span> <span class="s1">&#39;bool&#39;</span><span class="p">,</span> <span class="s1">&#39;breakpoint&#39;</span><span class="p">,</span> <span class="s1">&#39;bytearray&#39;</span><span class="p">,</span> <span class="s1">&#39;bytes&#39;</span><span class="p">,</span> <span class="s1">&#39;callable&#39;</span><span class="p">,</span> <span class="s1">&#39;chr&#39;</span><span class="p">,</span> <span class="s1">&#39;classmethod&#39;</span><span class="p">,</span> <span class="s1">&#39;compile&#39;</span><span class="p">,</span> <span class="s1">&#39;complex&#39;</span><span class="p">,</span> <span class="s1">&#39;copyright&#39;</span><span class="p">,</span> <span class="s1">&#39;credits&#39;</span><span class="p">,</span> <span class="s1">&#39;delattr&#39;</span><span class="p">,</span> <span class="s1">&#39;dict&#39;</span><span class="p">,</span> <span class="s1">&#39;dir&#39;</span><span class="p">,</span> <span class="s1">&#39;divmod&#39;</span><span class="p">,</span> <span class="s1">&#39;enumerate&#39;</span><span class="p">,</span> <span class="s1">&#39;eval&#39;</span><span class="p">,</span> <span class="s1">&#39;exec&#39;</span><span class="p">,</span> <span class="s1">&#39;exit&#39;</span><span class="p">,</span> <span class="s1">&#39;filter&#39;</span><span class="p">,</span> <span class="s1">&#39;float&#39;</span><span class="p">,</span> <span class="s1">&#39;format&#39;</span><span class="p">,</span> <span class="s1">&#39;frozenset&#39;</span><span class="p">,</span> <span class="s1">&#39;getattr&#39;</span><span class="p">,</span> <span class="s1">&#39;globals&#39;</span><span class="p">,</span> <span class="s1">&#39;hasattr&#39;</span><span class="p">,</span> <span class="s1">&#39;hash&#39;</span><span class="p">,</span> <span class="s1">&#39;help&#39;</span><span class="p">,</span> <span class="s1">&#39;hex&#39;</span><span class="p">,</span> <span class="s1">&#39;id&#39;</span><span class="p">,</span> <span class="s1">&#39;input&#39;</span><span class="p">,</span> <span class="s1">&#39;int&#39;</span><span class="p">,</span> <span class="s1">&#39;isinstance&#39;</span><span class="p">,</span> <span class="s1">&#39;issubclass&#39;</span><span class="p">,</span> <span class="s1">&#39;iter&#39;</span><span class="p">,</span> <span class="s1">&#39;len&#39;</span><span class="p">,</span> <span class="s1">&#39;license&#39;</span><span class="p">,</span> <span class="s1">&#39;list&#39;</span><span class="p">,</span> <span class="s1">&#39;locals&#39;</span><span class="p">,</span> <span class="s1">&#39;map&#39;</span><span class="p">,</span> <span class="s1">&#39;max&#39;</span><span class="p">,</span> <span class="s1">&#39;memoryview&#39;</span><span class="p">,</span> <span class="s1">&#39;min&#39;</span><span class="p">,</span> <span class="s1">&#39;next&#39;</span><span class="p">,</span> <span class="s1">&#39;object&#39;</span><span class="p">,</span> <span class="s1">&#39;oct&#39;</span><span class="p">,</span> <span class="s1">&#39;open&#39;</span><span class="p">,</span> <span class="s1">&#39;ord&#39;</span><span class="p">,</span> <span class="s1">&#39;pow&#39;</span><span class="p">,</span> <span class="s1">&#39;print&#39;</span><span class="p">,</span> <span class="s1">&#39;property&#39;</span><span class="p">,</span> <span class="s1">&#39;quit&#39;</span><span class="p">,</span> <span class="s1">&#39;range&#39;</span><span class="p">,</span> <span class="s1">&#39;repr&#39;</span><span class="p">,</span> <span class="s1">&#39;reversed&#39;</span><span class="p">,</span> <span class="s1">&#39;round&#39;</span><span class="p">,</span> <span class="s1">&#39;set&#39;</span><span class="p">,</span> <span class="s1">&#39;setattr&#39;</span><span class="p">,</span> <span class="s1">&#39;slice&#39;</span><span class="p">,</span> <span class="s1">&#39;sorted&#39;</span><span class="p">,</span> <span class="s1">&#39;staticmethod&#39;</span><span class="p">,</span> <span class="s1">&#39;str&#39;</span><span class="p">,</span> <span class="s1">&#39;sum&#39;</span><span class="p">,</span> <span class="s1">&#39;super&#39;</span><span class="p">,</span> <span class="s1">&#39;tuple&#39;</span><span class="p">,</span> <span class="s1">&#39;type&#39;</span><span class="p">,</span> <span class="s1">&#39;vars&#39;</span><span class="p">,</span> <span class="s1">&#39;zip&#39;</span><span class="p">]</span> +</code></pre></div> + +<p>Trong <a href="https://docs.python.org/3/reference/executionmodel.html?highlight=__builtins__#builtins-and-restricted-execution">tài liệu</a>&nbsp;viết</p> +<blockquote> +<p>CPython implementation detail: Users should not touch <strong>builtins</strong>; it is strictly an implementation detail. Users wanting to override values in the builtins namespace should import the builtins module and modify its attributes&nbsp;appropriately.</p> +</blockquote> +<p><strong>should not</strong> không có nghĩa là <strong>cannot</strong>, thử gán cho <code>__builtins__</code> một module&nbsp;khác:</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">os</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">__builtins__</span> <span class="o">=</span> <span class="n">os</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">getcwd</span><span class="p">()</span> +<span class="s1">&#39;/home/hvn&#39;</span> +<span class="o">&gt;&gt;&gt;</span> <span class="nb">globals</span><span class="p">()</span> +<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span> + <span class="n">File</span> <span class="s2">&quot;&lt;stdin&gt;&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span> +<span class="ne">NameError</span><span class="p">:</span> <span class="n">name</span> <span class="s1">&#39;globals&#39;</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">defined</span> +<span class="o">&gt;&gt;&gt;</span> <span class="nb">print</span> +<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span> + <span class="n">File</span> <span class="s2">&quot;&lt;stdin&gt;&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span> +<span class="ne">NameError</span><span class="p">:</span> <span class="n">name</span> <span class="s1">&#39;print&#39;</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">defined</span> +</code></pre></div> + +<p>các builtin function như print hay globals không còn nữa, thay vào đó là các funtion trong module <code>os</code>.</p> +<p>Bất ngờ&nbsp;chưa?!</p> +<h3>Kết&nbsp;luận</h3> +<p>Python vẫn luôn có những bí mật rất không muốn bật&nbsp;mí.</p> +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Lớp Python PyMivn khóa 44 tại Hà Nội khai giảng 11/5/20232023-05-07T11:00:00+07:002023-05-07T11:00:00+07:00Pymier0tag:n.pymi.vn,2023-05-07:/pymi2305.html<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để trở thành lập trình viên #python chuyên&nbsp;nghiệp.</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1682686581854-5e71f58e7e3f?ixlib=rb-4.0.3&amp;q=85&amp;fm=jpg&amp;crop=entropy&amp;cs=srgb&amp;dl=neom-wUyMk7ziLT0-unsplash.jpg&amp;w=640"></p> +<p>Photo by <a href="https://unsplash.com/@neom?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText"><span class="caps">NEOM</span></a> on <a href="https://unsplash.com/photos/wUyMk7ziLT0?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></p> +<p>Học viên không ở Hà Nội có thể học qua livestream trên Youtube với kênh chat +Slack cùng các học viên Hà&nbsp;Nội.</p> +<p>Chi tiết tại trang chủ <a href="https://pymi.vn">Pymivn</a>. +Thắc …</p><p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để trở thành lập trình viên #python chuyên&nbsp;nghiệp.</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1682686581854-5e71f58e7e3f?ixlib=rb-4.0.3&amp;q=85&amp;fm=jpg&amp;crop=entropy&amp;cs=srgb&amp;dl=neom-wUyMk7ziLT0-unsplash.jpg&amp;w=640"></p> +<p>Photo by <a href="https://unsplash.com/@neom?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText"><span class="caps">NEOM</span></a> on <a href="https://unsplash.com/photos/wUyMk7ziLT0?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></p> +<p>Học viên không ở Hà Nội có thể học qua livestream trên Youtube với kênh chat +Slack cùng các học viên Hà&nbsp;Nội.</p> +<p>Chi tiết tại trang chủ <a href="https://pymi.vn">Pymivn</a>. +Thắc mắc/hỏi đáp: <a href="https://invite.pymi.vn">slack</a></p>zip ngắn, zip dài2023-04-28T00:00:00+07:002023-04-28T00:00:00+07:00Pymier0tag:n.pymi.vn,2023-04-28:/zip.html<p>zip (cái khóa kéo) là một function built-in sẵn trong Python, để có được vị trí này - ngang ngửa với len, và print, zip rõ ràng là rất quan&nbsp;trọng.</p> +<p><img alt="zip" src="https://pixabay.com/get/g5abfdeab5cb385898885ff39db03f6f20df8e2d03a4a4933afe9f19c5c9f47be5a862afdf0e0d00fabfd1c45ce36e3c50828f18dcf0a85119e934044fc0a3309d42d89f1271d4219b3261ed73ed55ef2_640.jpg"></p> +<h2>zip - builtin&nbsp;function</h2> +<div class="highlight"><pre><span></span><code><span class="n">Init</span><span class="w"> </span><span class="n">signature</span><span class="o">:</span><span class="w"> </span><span class="n">zip</span><span class="p">(</span><span class="kr">self</span><span class="p">,</span><span class="w"> </span><span class="o">/</span><span class="p">,</span><span class="w"> </span><span class="o">*</span><span class="n">args</span><span class="p">,</span><span class="w"> </span><span class="o">**</span><span class="n">kwargs</span><span class="p">)</span><span class="w"></span> +<span class="n">Docstring</span><span class="o">:</span><span class="w"></span> +<span class="n">zip</span><span class="p">(</span><span class="o">*</span><span class="n">iterables</span><span class="p">)</span><span class="w"> </span><span class="o">--&gt;</span><span class="w"> </span><span class="n">A</span><span class="w"> </span><span class="n">zip</span><span class="w"> </span><span class="n">object</span><span class="w"> </span><span class="n">yielding</span><span class="w"> </span><span class="n">tuples</span><span class="w"> </span><span class="n">until</span><span class="w"> </span><span class="n">an</span><span class="w"> </span><span class="n">input …</span></code></pre></div><p>zip (cái khóa kéo) là một function built-in sẵn trong Python, để có được vị trí này - ngang ngửa với len, và print, zip rõ ràng là rất quan&nbsp;trọng.</p> +<p><img alt="zip" src="https://pixabay.com/get/g5abfdeab5cb385898885ff39db03f6f20df8e2d03a4a4933afe9f19c5c9f47be5a862afdf0e0d00fabfd1c45ce36e3c50828f18dcf0a85119e934044fc0a3309d42d89f1271d4219b3261ed73ed55ef2_640.jpg"></p> +<h2>zip - builtin&nbsp;function</h2> +<div class="highlight"><pre><span></span><code><span class="n">Init</span><span class="w"> </span><span class="n">signature</span><span class="o">:</span><span class="w"> </span><span class="n">zip</span><span class="p">(</span><span class="kr">self</span><span class="p">,</span><span class="w"> </span><span class="o">/</span><span class="p">,</span><span class="w"> </span><span class="o">*</span><span class="n">args</span><span class="p">,</span><span class="w"> </span><span class="o">**</span><span class="n">kwargs</span><span class="p">)</span><span class="w"></span> +<span class="n">Docstring</span><span class="o">:</span><span class="w"></span> +<span class="n">zip</span><span class="p">(</span><span class="o">*</span><span class="n">iterables</span><span class="p">)</span><span class="w"> </span><span class="o">--&gt;</span><span class="w"> </span><span class="n">A</span><span class="w"> </span><span class="n">zip</span><span class="w"> </span><span class="n">object</span><span class="w"> </span><span class="n">yielding</span><span class="w"> </span><span class="n">tuples</span><span class="w"> </span><span class="n">until</span><span class="w"> </span><span class="n">an</span><span class="w"> </span><span class="n">input</span><span class="w"> </span><span class="n">is</span><span class="w"> </span><span class="n">exhausted</span><span class="p">.</span><span class="w"></span> + +<span class="w"> </span><span class="o">&gt;&gt;&gt;</span><span class="w"> </span><span class="n">list</span><span class="p">(</span><span class="n">zip</span><span class="p">(</span><span class="s">&#39;abcdefg&#39;</span><span class="p">,</span><span class="w"> </span><span class="nf">range</span><span class="p">(</span><span class="mi">3</span><span class="p">),</span><span class="w"> </span><span class="nf">range</span><span class="p">(</span><span class="mi">4</span><span class="p">)))</span><span class="w"></span> +<span class="w"> </span><span class="p">[(</span><span class="s">&#39;a&#39;</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">),</span><span class="w"> </span><span class="p">(</span><span class="s">&#39;b&#39;</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">),</span><span class="w"> </span><span class="p">(</span><span class="s">&#39;c&#39;</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">)]</span><span class="w"></span> + +<span class="n">The</span><span class="w"> </span><span class="n">zip</span><span class="w"> </span><span class="n">object</span><span class="w"> </span><span class="n">yields</span><span class="w"> </span><span class="n">n</span><span class="o">-</span><span class="nf">length</span><span class="w"> </span><span class="n">tuples</span><span class="p">,</span><span class="w"> </span><span class="n">where</span><span class="w"> </span><span class="n">n</span><span class="w"> </span><span class="n">is</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">number</span><span class="w"> </span><span class="kr">of</span><span class="w"> </span><span class="n">iterables</span><span class="w"></span> +<span class="n">passed</span><span class="w"> </span><span class="kr">as</span><span class="w"> </span><span class="n">positional</span><span class="w"> </span><span class="n">arguments</span><span class="w"> </span><span class="n">to</span><span class="w"> </span><span class="n">zip</span><span class="p">().</span><span class="w"> </span><span class="n">The</span><span class="w"> </span><span class="n">i</span><span class="o">-</span><span class="n">th</span><span class="w"> </span><span class="n">element</span><span class="w"> </span><span class="kr">in</span><span class="w"> </span><span class="n">every</span><span class="w"> </span><span class="n">tuple</span><span class="w"></span> +<span class="n">comes</span><span class="w"> </span><span class="n">from</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">i</span><span class="o">-</span><span class="n">th</span><span class="w"> </span><span class="n">iterable</span><span class="w"> </span><span class="n">argument</span><span class="w"> </span><span class="n">to</span><span class="w"> </span><span class="n">zip</span><span class="p">().</span><span class="w"> </span><span class="n">This</span><span class="w"> </span><span class="n">continues</span><span class="w"> </span><span class="n">until</span><span class="w"> </span><span class="n">the</span><span class="w"></span> +<span class="n">shortest</span><span class="w"> </span><span class="n">argument</span><span class="w"> </span><span class="n">is</span><span class="w"> </span><span class="n">exhausted</span><span class="p">.</span><span class="w"></span> +</code></pre></div> + +<p>zip nhận vào 2 hay nhiều iterable (như list, string &#8230;) và trả về lần lượt tuple chứa các phần tử thứ i của tất cả&nbsp;iterable.</p> +<p>Ví&nbsp;dụ:</p> +<div class="highlight"><pre><span></span><code><span class="n">In</span> <span class="p">[</span><span class="mi">3</span><span class="p">]:</span> <span class="nb">list</span><span class="p">(</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="mi">3</span><span class="p">],</span> <span class="s2">&quot;Python&quot;</span><span class="p">))</span> +<span class="n">Out</span><span class="p">[</span><span class="mi">3</span><span class="p">]:</span> <span class="p">[(</span><span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;P&#39;</span><span class="p">),</span> <span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s1">&#39;y&#39;</span><span class="p">),</span> <span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="s1">&#39;t&#39;</span><span class="p">)]</span> +</code></pre></div> + +<p><code>zip</code> dừng lại khi iterable ngắn nhất kết&nbsp;thúc.</p> +<p>Vậy làm sao tiếp tục với iterable dài, và gán giá trị mặc định cho các iterable ngắn&nbsp;hơn?</p> +<h3>itertools.zip_longest</h3> +<p>Thư viện có sẵn <code>itertools</code> cung cấp thêm nhiều công cụ để &#8220;iterate&#8221;, +với function zip_longest, thay vì dừng lại ở iterable ngắn nhất, nó chạy tới khi iterable dài nhất kết&nbsp;thúc:</p> +<div class="highlight"><pre><span></span><code><span class="n">In</span> <span class="p">[</span><span class="mi">4</span><span class="p">]:</span> <span class="kn">import</span> <span class="nn">itertools</span> + +<span class="n">In</span> <span class="p">[</span><span class="mi">6</span><span class="p">]:</span> <span class="nb">list</span><span class="p">(</span><span class="n">itertools</span><span class="o">.</span><span class="n">zip_longest</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="s2">&quot;Python&quot;</span><span class="p">))</span> +<span class="n">Out</span><span class="p">[</span><span class="mi">6</span><span class="p">]:</span> <span class="p">[(</span><span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;P&#39;</span><span class="p">),</span> <span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s1">&#39;y&#39;</span><span class="p">),</span> <span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="s1">&#39;t&#39;</span><span class="p">),</span> <span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="s1">&#39;h&#39;</span><span class="p">),</span> <span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="s1">&#39;o&#39;</span><span class="p">),</span> <span class="p">(</span><span class="kc">None</span><span class="p">,</span> <span class="s1">&#39;n&#39;</span><span class="p">)]</span> +</code></pre></div> + +<p>mặc định điền các phần tử thiếu của iterable ngắn là <code>None</code>.</p> +<p>Giá trị mặc định này có thể thay đổi bằng&nbsp;fillvalue:</p> +<div class="highlight"><pre><span></span><code><span class="n">In</span> <span class="p">[</span><span class="mi">9</span><span class="p">]:</span> <span class="nb">list</span><span class="p">(</span><span class="n">itertools</span><span class="o">.</span><span class="n">zip_longest</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="s2">&quot;Python&quot;</span><span class="p">,</span> <span class="n">fillvalue</span><span class="o">=</span><span class="mi">0</span><span class="p">))</span> +<span class="n">Out</span><span class="p">[</span><span class="mi">9</span><span class="p">]:</span> <span class="p">[(</span><span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;P&#39;</span><span class="p">),</span> <span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s1">&#39;y&#39;</span><span class="p">),</span> <span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="s1">&#39;t&#39;</span><span class="p">),</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="s1">&#39;h&#39;</span><span class="p">),</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="s1">&#39;o&#39;</span><span class="p">),</span> <span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="s1">&#39;n&#39;</span><span class="p">)]</span> +</code></pre></div> + +<h3>Kết&nbsp;luận</h3> +<p>Python hàng xịn, zip ngắn zip dài có đủ cả&nbsp;hai.</p> +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>http dùng requests? pycurl nhanh gấp 2!2023-04-04T00:00:00+07:002023-04-04T00:00:00+07:00Pymier0tag:n.pymi.vn,2023-04-04:/pycurl.html<p><img alt="curl" src="https://curl.se/logo/curl-logo.svg"></p> +<h3>curl - câu lệnh <span class="caps">HTTP</span> client phổ biến&nbsp;nhất</h3> +<p>Đó là điều không phải bàn cãi. Đa phần các lập trình viên hay các devops/sysadmin đều ít nhiều biết dùng&nbsp;curl.</p> +<p>Cài đặt: <code>sudo apt install curl</code></p> +<div class="highlight"><pre><span></span><code><span class="err">$</span> <span class="n">curl</span> <span class="o">-</span><span class="n">XGET</span> <span class="n">https</span><span class="p">:</span><span class="o">//</span><span class="n">httpbin</span><span class="o">.</span><span class="n">org</span><span class="o">/</span><span class="n">get</span> +<span class="p">{</span> + <span class="s2">&quot;args&quot;</span><span class="p">:</span> <span class="p">{},</span> + <span class="s2">&quot;headers&quot;</span><span class="p">:</span> <span class="p">{</span> + <span class="s2">&quot;Accept&quot;</span><span class="p">:</span> <span class="s2">&quot;*/*&quot;</span><span class="p">,</span> + <span class="s2">&quot;Host&quot;</span><span class="p">:</span> <span class="s2">&quot;httpbin.org&quot;</span><span class="p">,</span> + <span class="s2">&quot;User-Agent …</span></code></pre></div><p><img alt="curl" src="https://curl.se/logo/curl-logo.svg"></p> +<h3>curl - câu lệnh <span class="caps">HTTP</span> client phổ biến&nbsp;nhất</h3> +<p>Đó là điều không phải bàn cãi. Đa phần các lập trình viên hay các devops/sysadmin đều ít nhiều biết dùng&nbsp;curl.</p> +<p>Cài đặt: <code>sudo apt install curl</code></p> +<div class="highlight"><pre><span></span><code><span class="err">$</span> <span class="n">curl</span> <span class="o">-</span><span class="n">XGET</span> <span class="n">https</span><span class="p">:</span><span class="o">//</span><span class="n">httpbin</span><span class="o">.</span><span class="n">org</span><span class="o">/</span><span class="n">get</span> +<span class="p">{</span> + <span class="s2">&quot;args&quot;</span><span class="p">:</span> <span class="p">{},</span> + <span class="s2">&quot;headers&quot;</span><span class="p">:</span> <span class="p">{</span> + <span class="s2">&quot;Accept&quot;</span><span class="p">:</span> <span class="s2">&quot;*/*&quot;</span><span class="p">,</span> + <span class="s2">&quot;Host&quot;</span><span class="p">:</span> <span class="s2">&quot;httpbin.org&quot;</span><span class="p">,</span> + <span class="s2">&quot;User-Agent&quot;</span><span class="p">:</span> <span class="s2">&quot;curl/7.68.0&quot;</span><span class="p">,</span> + <span class="s2">&quot;X-Amzn-Trace-Id&quot;</span><span class="p">:</span> <span class="s2">&quot;Root=1-642c2f63-1e6528c964ff9d9b39d7dc7f&quot;</span> + <span class="p">},</span> + <span class="s2">&quot;origin&quot;</span><span class="p">:</span> <span class="s2">&quot;14.1.11.1&quot;</span><span class="p">,</span> + <span class="s2">&quot;url&quot;</span><span class="p">:</span> <span class="s2">&quot;https://httpbin.org/get&quot;</span> +<span class="p">}</span> +</code></pre></div> + +<p>Chương trình này sử dụng thư viện lo tất cả bên dưới:&nbsp;libcurl</p> +<div class="highlight"><pre><span></span><code>$ ldd <span class="sb">`</span>which curl<span class="sb">`</span> <span class="p">|</span> grep curl + libcurl.so.4 <span class="o">=</span>&gt; /lib/x86_64-linux-gnu/libcurl.so.4 <span class="o">(</span>0x00007f0a91282000<span class="o">)</span> +</code></pre></div> + +<p><code>libcurl</code> có &#8220;binding&#8221; cho hầu hết tất cả các ngôn ngữ lập&nbsp;trình.</p> +<p>Đếm đơn giản có hơn 50 ngôn ngữ: <span class="caps">PHP</span>, Java, C, Rust, &#8230; và cả&nbsp;Python</p> +<div class="highlight"><pre><span></span><code>$ curl https://curl.se/libcurl/bindings.html <span class="p">|</span> grep <span class="s1">&#39;&lt;a href=&quot;http&#39;</span> -c +<span class="m">57</span> +</code></pre></div> + +<h3>pycurl - code dài x2 nhưng nhanh cũng x2 lần&nbsp;requests</h3> +<p>requests phổ biến nhờ <span class="caps">API</span> dễ dùng requests.get requests.post &#8230; viết hoàn tòan bằng Python. Ưu điểm này cũng chỉ ra yếu điểm của Python: chậm khi so với&nbsp;C.</p> +<p>Nếu viết 1 script gọi 10 requests nhỏ nhỏ chạy cỡ 10s, viết lại bằng pycurl có thể mất 5s, việc này không có gì đáng kể, nên vẫn sẽ dùng&nbsp;requests.</p> +<p>Nhưng với chương trình phải <span class="caps">GET</span>/<span class="caps">POST</span> hàng trăm ngàn requests mỗi giờ, hay gửi vài <span class="caps">GB</span>/<span class="caps">TB</span>, thay pycurl sẽ mang lại cải thiện đáng kể. +Benchmark tại trang chủ pycurl: <a href="https://github.com/svanoort/python-client-benchmarks">https://github.com/svanoort/python-client-benchmarks</a></p> +<p>Giải thích khi nào dùng pycurl vs request <a href="http://pycurl.io/docs/latest/index.html#pycurl-vs-requests">http://pycurl.io/docs/latest/index.html#pycurl-vs-requests</a></p> +<p>Ví dụ đơn giản với 3 dòng requests và 10 dòng&nbsp;pycurl:</p> +<p>Trên Ubuntu 20.04 cài&nbsp;đặt:</p> +<div class="highlight"><pre><span></span><code>sudo apt install -y libcurl4-openssl-dev +</code></pre></div> + +<div class="highlight"><pre><span></span><code>python3 -m venv cenv +. cenv/bin/activate +python3 -m pip install pycurl requests +</code></pre></div> + +<h4>requests ngắn và&nbsp;chậm</h4> +<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">requests</span> +<span class="n">r</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;https://pymi.vn&quot;</span><span class="p">)</span> +<span class="nb">print</span><span class="p">(</span><span class="n">r</span><span class="o">.</span><span class="n">text</span><span class="p">[:</span><span class="mi">200</span><span class="p">])</span> +</code></pre></div> + +<p>Tốc&nbsp;độ:</p> +<div class="highlight"><pre><span></span><code>$ /usr/bin/time -v python3 req.py +<span class="cp">&lt;!DOCTYPE html&gt;</span> +<span class="p">&lt;</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">&quot;vi&quot;</span><span class="p">&gt;</span> +<span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span> +<span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&quot;utf-8&quot;</span><span class="p">&gt;</span> +<span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>Học lập trình Python — PyMI.vn<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span> +<span class="p">&lt;</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">&quot;viewport&quot;</span> <span class="na">content</span><span class="o">=</span><span class="s">&quot;width=device-width, initial-scale=1.0&quot;</span> <span class="p">/&gt;</span> +<span class="p">&lt;</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">&quot;descri</span> +<span class="s"> Command being timed: &quot;</span><span class="na">python3</span> <span class="na">req</span><span class="err">.</span><span class="na">py</span><span class="err">&quot;</span> + <span class="na">User</span> <span class="na">time</span> <span class="err">(</span><span class="na">seconds</span><span class="err">)</span><span class="na">:</span> <span class="na">0</span><span class="err">.</span><span class="na">10</span> + <span class="na">System</span> <span class="na">time</span> <span class="err">(</span><span class="na">seconds</span><span class="err">)</span><span class="na">:</span> <span class="na">0</span><span class="err">.</span><span class="na">01</span> + <span class="na">Percent</span> <span class="na">of</span> <span class="na">CPU</span> <span class="na">this</span> <span class="na">job</span> <span class="na">got:</span> <span class="na">20</span><span class="err">%</span> + <span class="na">Elapsed</span> <span class="err">(</span><span class="na">wall</span> <span class="na">clock</span><span class="err">)</span> <span class="na">time</span> <span class="err">(</span><span class="na">h:mm:ss</span> <span class="na">or</span> <span class="na">m:ss</span><span class="err">)</span><span class="na">:</span> <span class="na">0:00</span><span class="err">.</span><span class="na">58</span> + <span class="na">Average</span> <span class="na">shared</span> <span class="na">text</span> <span class="na">size</span> <span class="err">(</span><span class="na">kbytes</span><span class="err">)</span><span class="na">:</span> <span class="na">0</span> + <span class="na">Average</span> <span class="na">unshared</span> <span class="na">data</span> <span class="na">size</span> <span class="err">(</span><span class="na">kbytes</span><span class="err">)</span><span class="na">:</span> <span class="na">0</span> + <span class="na">Average</span> <span class="na">stack</span> <span class="na">size</span> <span class="err">(</span><span class="na">kbytes</span><span class="err">)</span><span class="na">:</span> <span class="na">0</span> + <span class="na">Average</span> <span class="na">total</span> <span class="na">size</span> <span class="err">(</span><span class="na">kbytes</span><span class="err">)</span><span class="na">:</span> <span class="na">0</span> + <span class="na">Maximum</span> <span class="na">resident</span> <span class="na">set</span> <span class="na">size</span> <span class="err">(</span><span class="na">kbytes</span><span class="err">)</span><span class="na">:</span> <span class="na">23152</span> +</code></pre></div> + +<h4>pycurl dài và&nbsp;nhanh</h4> +<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">pycurl</span> +<span class="kn">from</span> <span class="nn">io</span> <span class="kn">import</span> <span class="n">BytesIO</span> +<span class="n">buff</span> <span class="o">=</span> <span class="n">BytesIO</span><span class="p">()</span> +<span class="n">c</span> <span class="o">=</span> <span class="n">pycurl</span><span class="o">.</span><span class="n">Curl</span><span class="p">()</span> +<span class="n">c</span><span class="o">.</span><span class="n">setopt</span><span class="p">(</span><span class="n">c</span><span class="o">.</span><span class="n">URL</span><span class="p">,</span> <span class="s1">&#39;https://pymi.vn&#39;</span><span class="p">)</span> +<span class="n">c</span><span class="o">.</span><span class="n">setopt</span><span class="p">(</span><span class="n">c</span><span class="o">.</span><span class="n">WRITEDATA</span><span class="p">,</span> <span class="n">buff</span><span class="p">)</span> +<span class="n">c</span><span class="o">.</span><span class="n">setopt</span><span class="p">(</span><span class="n">c</span><span class="o">.</span><span class="n">HTTPHEADER</span><span class="p">,</span> <span class="p">[</span><span class="s1">&#39;User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0/8mqLkJuL-86&#39;</span><span class="p">])</span> +<span class="n">c</span><span class="o">.</span><span class="n">perform</span><span class="p">()</span> +<span class="n">c</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> +<span class="n">body</span> <span class="o">=</span> <span class="n">buff</span><span class="o">.</span><span class="n">getvalue</span><span class="p">()</span> +<span class="nb">print</span><span class="p">(</span><span class="n">body</span><span class="o">.</span><span class="n">decode</span><span class="p">()[:</span><span class="mi">200</span><span class="p">])</span> +</code></pre></div> + +<p>Tốc&nbsp;độ</p> +<div class="highlight"><pre><span></span><code>$ /usr/bin/time -v python3 curl.py +<span class="cp">&lt;!DOCTYPE html&gt;</span> +<span class="p">&lt;</span><span class="nt">html</span> <span class="na">lang</span><span class="o">=</span><span class="s">&quot;vi&quot;</span><span class="p">&gt;</span> +<span class="p">&lt;</span><span class="nt">head</span><span class="p">&gt;</span> +<span class="p">&lt;</span><span class="nt">meta</span> <span class="na">charset</span><span class="o">=</span><span class="s">&quot;utf-8&quot;</span><span class="p">&gt;</span> +<span class="p">&lt;</span><span class="nt">title</span><span class="p">&gt;</span>Học lập trình Python — PyMI.vn<span class="p">&lt;/</span><span class="nt">title</span><span class="p">&gt;</span> +<span class="p">&lt;</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">&quot;viewport&quot;</span> <span class="na">content</span><span class="o">=</span><span class="s">&quot;width=device-width, initial-scale=1.0&quot;</span> <span class="p">/&gt;</span> +<span class="p">&lt;</span><span class="nt">meta</span> <span class="na">name</span><span class="o">=</span><span class="s">&quot;descri</span> +<span class="s"> Command being timed: &quot;</span><span class="na">python3</span> <span class="na">curl</span><span class="err">.</span><span class="na">py</span><span class="err">&quot;</span> + <span class="na">User</span> <span class="na">time</span> <span class="err">(</span><span class="na">seconds</span><span class="err">)</span><span class="na">:</span> <span class="na">0</span><span class="err">.</span><span class="na">05</span> + <span class="na">System</span> <span class="na">time</span> <span class="err">(</span><span class="na">seconds</span><span class="err">)</span><span class="na">:</span> <span class="na">0</span><span class="err">.</span><span class="na">00</span> + <span class="na">Percent</span> <span class="na">of</span> <span class="na">CPU</span> <span class="na">this</span> <span class="na">job</span> <span class="na">got:</span> <span class="na">9</span><span class="err">%</span> + <span class="na">Elapsed</span> <span class="err">(</span><span class="na">wall</span> <span class="na">clock</span><span class="err">)</span> <span class="na">time</span> <span class="err">(</span><span class="na">h:mm:ss</span> <span class="na">or</span> <span class="na">m:ss</span><span class="err">)</span><span class="na">:</span> <span class="na">0:00</span><span class="err">.</span><span class="na">60</span> + <span class="na">Average</span> <span class="na">shared</span> <span class="na">text</span> <span class="na">size</span> <span class="err">(</span><span class="na">kbytes</span><span class="err">)</span><span class="na">:</span> <span class="na">0</span> + <span class="na">Average</span> <span class="na">unshared</span> <span class="na">data</span> <span class="na">size</span> <span class="err">(</span><span class="na">kbytes</span><span class="err">)</span><span class="na">:</span> <span class="na">0</span> + <span class="na">Average</span> <span class="na">stack</span> <span class="na">size</span> <span class="err">(</span><span class="na">kbytes</span><span class="err">)</span><span class="na">:</span> <span class="na">0</span> + <span class="na">Average</span> <span class="na">total</span> <span class="na">size</span> <span class="err">(</span><span class="na">kbytes</span><span class="err">)</span><span class="na">:</span> <span class="na">0</span> + <span class="na">Maximum</span> <span class="na">resident</span> <span class="na">set</span> <span class="na">size</span> <span class="err">(</span><span class="na">kbytes</span><span class="err">)</span><span class="na">:</span> <span class="na">20576</span> +</code></pre></div> + +<p><span class="caps">PS</span>: tuy &#8220;khó dùng&#8221; hơn requests, một khi biết dùng, có thể dùng ở ~50 ngôn ngữ khác nhau là một lợi thế không hề nhỏ của&nbsp;libcurl.</p> +<h3>Kết&nbsp;luận</h3> +<p>Ngắn chậm hay dài nhanh? sự lựa chọn là của&nbsp;bạn.</p> +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Làm it bao giờ có 2 tỷ mua nhà?2023-02-12T00:00:00+07:002023-02-12T00:00:00+07:00Pymier0tag:n.pymi.vn,2023-02-12:/2ty.html<p>Năm 2 sau <span class="caps">COVID</span> nguyên, ngành <span class="caps">IT</span> đã bắt đầu trượt khỏi đỉnh khi hàng ngàn công ty công nghệ lớn nhỏ cắt giảm nhân sự trên toàn cầu. Nhưng trong xã hội, ngành <span class="caps">IT</span> vẫn là 1 ngành lương cao. Lương cao thì nhiều tiền, nhiều tiền thì mua …</p><p>Năm 2 sau <span class="caps">COVID</span> nguyên, ngành <span class="caps">IT</span> đã bắt đầu trượt khỏi đỉnh khi hàng ngàn công ty công nghệ lớn nhỏ cắt giảm nhân sự trên toàn cầu. Nhưng trong xã hội, ngành <span class="caps">IT</span> vẫn là 1 ngành lương cao. Lương cao thì nhiều tiền, nhiều tiền thì mua nhà, vậy làm bao lâu thì mua được&nbsp;nhà?</p> +<p>Bài viết tất nhiên không bàn đến các trường hợp ngoại lệ, hay những người trúng quả coin, startup thành công, viết app riêng vài triệu lượt tải hay đi nộp thuế chục tỷ mỗi năm. Chỉ tính tầng lớp công nhân lập trình lương tháng, tạm thời độc thân. +Cũng không bàn tới đi vay ngân hàng, vì vay thì cũng phải trả&nbsp;chứ.</p> +<h3>Mua nhà bao&nbsp;tiền</h3> +<p><img alt="stable diffusion house" src="https://n.pymi.vn/images/2ty.jpg"></p> +<p>Nhà có nhà thành phố, nhà trên núi, nhà biệt thự, nhà cấp 4, vậy nên nói mua nhà thì chung quá, nên lấy một ví dụ điển hình là mua nhà chung cư ở Hà Nội, khu vực không trung tâm, với mức giá khoảng 2 tỷ. +Lên tìm trên các trang bất động sản có thể&nbsp;thấy</p> +<blockquote> +<p>Nhỉnh 2 tỷ sở hữu ngay căn hộ siêu vip tại Goldsilk Complex dt 68m2, 2 pn - 1wc. Giá: 2,35&nbsp;tỷ</p> +</blockquote> +<p>2 tỷ 350 là hơn 2 tỷ RẤT NHIỀU (350 triệu mua được 1 cái ô tô), vậy cứ giả vờ là 2 tỷ&nbsp;tròn.</p> +<h3><span class="caps">IT</span> lương cao là bao&nbsp;nhiêu</h3> +<p>Theo <a href="https://topdev.vn/blog/muc-luong-cac-vi-tri-lap-trinh-2022/">Báo cáo thị trường <span class="caps">IT</span> Việt Nam – Tech Hiring 2022 của&nbsp;Topdev.vn</a></p> +<blockquote> +<p>Mức lương Lập trình viên làm về các công nghệ thuộc nhóm High Tech bao gồm Tensor Flows, Kubernetes, Python, lần lượt đạt mức $1,732, $1,669, $1,389. +Lập trình viên Senior có mức lương dao động từ $860 đến $1.510. Các vị trí Quản lý (từ 5 năm trở lên) hoặc cấp cao hơn được khảo sát có mức lương từ $1.410 cho đến hơn&nbsp;$2.300.</p> +</blockquote> +<p>Senior tức cỡ tầm 5,7 năm kinh nghiệm++, và giả sử có mức lương mơ ước là $2000&nbsp;gross.</p> +<h3>Lương $2000 bao giờ có 2 tỷ mua&nbsp;nhà?</h3> +<p>$2000 tại thời điểm viết bài có giá trị 47,142,249 <span class="caps">VND</span>, đây là lương gross, đổi sang Net (dùng các trang web trên mạng) còn&nbsp;38,879,871.</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="mi">2_000_000_000</span> <span class="o">/</span> <span class="mi">38_900_000</span> +<span class="mf">51.41388174807198</span> +</code></pre></div> + +<p>cỡ khoảng 51 tháng, coi như 1 năm có 13 tháng lương (1 tháng thưởng tết), vậy là 4&nbsp;năm.</p> +<p>Vậy sau 4 năm chỉ làm như cái máy kiếm tiền, không ăn uống, không tiêu, không mua gì, giá trị đồng tiền không đổi, thì bạn sẽ có 2 tỷ mua&nbsp;nhà!!!</p> +<h3>Không ăn sao&nbsp;sống?</h3> +<p>Chính vì thế, trừ đi tiền nhà thuê, ăn uống, tiêu dùng, mua sắm, nếu mất 1 nửa, thì số năm tăng lên là 8&nbsp;năm.</p> +<p>Chú ý bạn chỉ có lương $2000 sau khi có 5 năm kinh nghiệm, tức khi đủ tiền, là bạn có 5 + 8 == 13 năm đi làm. Đi làm từ năm 23 tuổi, thì khi đủ tiền mua nhà là 23 + 13 =&nbsp;36.</p> +<p>Đó là tính đơn giản, còn nếu lập gia đình, mua bỉm, mua sữa, mua sắm, đóng tiền học&#8230; thì còn lâu&nbsp;nữa.</p> +<p>Vậy bao giờ thì đủ tiền&nbsp;mua?</p> +<p>Chắc&nbsp;40.</p> +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Lớp Python PyMivn khóa 43 tại Hà Nội khai giảng 16/2/20232023-02-09T00:00:00+07:002023-02-09T00:00:00+07:00Pymier0tag:n.pymi.vn,2023-02-09:/pymi2302.html<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để trở thành lập trình viên #python chuyên&nbsp;nghiệp.</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1665602878676-219e01293b51?ixlib=rb-1.2.1&amp;dl=8machine-_-LhNDEs4MP5w-unsplash.jpg&amp;w=640&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb"></p> +<p>Photo by <a href="https://unsplash.com/@8machine_?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">8machine _</a> on <a href="https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></p> +<p>Học viên không ở Hà Nội có thể học qua livestream trên Youtube với kênh chat +Slack cùng các học viên Hà&nbsp;Nội.</p> +<p>Chi tiết tại trang chủ <a href="https://pymi.vn">Pymivn …</a></p><p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để trở thành lập trình viên #python chuyên&nbsp;nghiệp.</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1665602878676-219e01293b51?ixlib=rb-1.2.1&amp;dl=8machine-_-LhNDEs4MP5w-unsplash.jpg&amp;w=640&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb"></p> +<p>Photo by <a href="https://unsplash.com/@8machine_?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">8machine _</a> on <a href="https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></p> +<p>Học viên không ở Hà Nội có thể học qua livestream trên Youtube với kênh chat +Slack cùng các học viên Hà&nbsp;Nội.</p> +<p>Chi tiết tại trang chủ <a href="https://pymi.vn">Pymivn</a>. +Thắc mắc/hỏi đáp: <a href="https://invite.pymi.vn">slack</a></p>Thêm câu lệnh python vào gdb để in ra binary architecture2023-01-18T00:00:00+07:002023-01-18T00:00:00+07:00Pymier0tag:n.pymi.vn,2023-01-18:/hgdb-arch.html<h3>Architecture</h3> +<p>Mỗi file binary được compile cho một kiến trúc(architecture) <span class="caps">CPU</span> cụ&nbsp;thể.</p> +<p>Các architecture phổ biến&nbsp;như:</p> +<ul> +<li>x86-32 (32&nbsp;bits)</li> +<li>x86-64 (64&nbsp;bits)</li> +<li>arm-64 (phổ biến trên các máy điện thoại/máy tính bảng/hay Apple M1&nbsp;M2&#8230;)</li> +</ul> +<p>Một file binary đã compile sẵn cho x86-64 thì không …</p><h3>Architecture</h3> +<p>Mỗi file binary được compile cho một kiến trúc(architecture) <span class="caps">CPU</span> cụ&nbsp;thể.</p> +<p>Các architecture phổ biến&nbsp;như:</p> +<ul> +<li>x86-32 (32&nbsp;bits)</li> +<li>x86-64 (64&nbsp;bits)</li> +<li>arm-64 (phổ biến trên các máy điện thoại/máy tính bảng/hay Apple M1&nbsp;M2&#8230;)</li> +</ul> +<p>Một file binary đã compile sẵn cho x86-64 thì không thể chạy trực tiếp trên arm-64. +Ví dụ khi download <a href="https://github.com/prometheus/prometheus/releases/tag/v2.41.0">phần mềm prometheus thấy có sẵn file binary</a> cho từng architecture trên từng hệ điều&nbsp;hành.</p> +<p>Cách đơn giản nhất là gõ lệnh <code>file tenbinary</code>:</p> +<div class="highlight"><pre><span></span><code><span class="err">$</span><span class="w"> </span><span class="k">file</span><span class="w"> </span><span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="k">top</span><span class="w"> </span><span class="o">[</span><span class="n">0</span><span class="o">]</span><span class="w"></span> +<span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">bin</span><span class="o">/</span><span class="k">top</span><span class="err">:</span><span class="w"> </span><span class="n">ELF</span><span class="w"> </span><span class="mi">64</span><span class="o">-</span><span class="nc">bit</span><span class="w"> </span><span class="n">LSB</span><span class="w"> </span><span class="n">shared</span><span class="w"> </span><span class="k">object</span><span class="p">,</span><span class="w"> </span><span class="n">x86</span><span class="o">-</span><span class="mi">64</span><span class="p">,</span><span class="w"> </span><span class="n">version</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">(</span><span class="n">SYSV</span><span class="p">),</span><span class="w"> </span><span class="n">dynamically</span><span class="w"> </span><span class="n">linked</span><span class="p">,</span><span class="w"> </span><span class="n">interpreter</span><span class="w"> </span><span class="o">/</span><span class="n">lib64</span><span class="o">/</span><span class="n">ld</span><span class="o">-</span><span class="n">linux</span><span class="o">-</span><span class="n">x86</span><span class="o">-</span><span class="mf">64.</span><span class="n">so</span><span class="mf">.2</span><span class="p">,</span><span class="w"> </span><span class="n">BuildID</span><span class="o">[</span><span class="n">sha1</span><span class="o">]=</span><span class="mi">867346</span><span class="n">cd2ce3350129fc1e6fb923e92380eaf6e9</span><span class="p">,</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">GNU</span><span class="o">/</span><span class="n">Linux</span><span class="w"> </span><span class="mf">3.2.0</span><span class="p">,</span><span class="w"> </span><span class="n">stripped</span><span class="w"></span> +</code></pre></div> + +<p>thấy <code>x86-64</code>, <span class="caps">ELF</span> là format binary trên Linux, ở đây thấy <span class="caps">ELF</span>&nbsp;64-bit.</p> +<p>Trong gdb,&nbsp;gõ</p> +<div class="highlight"><pre><span></span><code><span class="ss">(</span><span class="nv">gdb</span><span class="ss">)</span><span class="w"> </span><span class="nv">maintenance</span><span class="w"> </span><span class="nv">info</span><span class="w"> </span><span class="nv">sections</span><span class="w"> </span>?<span class="w"></span> +<span class="k">Exec</span><span class="w"> </span><span class="nv">file</span>:<span class="w"></span> +<span class="w"> </span>`<span class="o">/</span><span class="nv">usr</span><span class="o">/</span><span class="nv">bin</span><span class="o">/</span><span class="nv">top</span><span class="err">&#39;, file type elf64-x86-64.</span><span class="w"></span> +</code></pre></div> + +<p>Câu lệnh này rất dài và khó nhớ, viết một câu lệnh mới tên là <code>arch</code> in ra <code>elf64-x86-64</code> sẽ ngắn gọn hơn&nbsp;nhiều.</p> +<h3>Thêm command arch vào&nbsp;gdb</h3> +<p>Đoạn code tham khảo từ <a href="https://github.com/longld/peda"><span class="caps">PEDA</span></a>, các bước thực hiện là chạy các câu lệnh của gdb +rồi lấy kết quả ra và in ra phần mong muốn. Thêm vào file code <code>/home/hvn/me/hgdb/hgdb.py</code> như <a href="https://n.pymi.vn/hgdb.html">bài trước</a>.</p> +<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">tempfile</span> +<span class="k">class</span> <span class="nc">Arch</span><span class="p">(</span><span class="n">gdb</span><span class="o">.</span><span class="n">Command</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="nb">super</span> <span class="p">(</span><span class="n">Arch</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="s2">&quot;arch&quot;</span><span class="p">,</span> <span class="n">gdb</span><span class="o">.</span><span class="n">COMMAND_USER</span><span class="p">)</span> + + + <span class="k">def</span> <span class="nf">invoke</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">arg</span><span class="p">,</span> <span class="n">from_tty</span><span class="p">):</span> + <span class="n">tmpfile</span> <span class="o">=</span> <span class="n">tempfile</span><span class="o">.</span><span class="n">mktemp</span><span class="p">()</span> + <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">tmpfile</span><span class="p">,</span> <span class="s1">&#39;w+&#39;</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span> + <span class="n">gdb</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="s2">&quot;set logging off&quot;</span><span class="p">)</span> + <span class="n">gdb</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="s2">&quot;set height 0&quot;</span><span class="p">)</span> + <span class="n">gdb</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;set logging file </span><span class="si">{</span><span class="n">tmpfile</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span> + <span class="n">gdb</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="s2">&quot;set logging overwrite on&quot;</span><span class="p">)</span> + <span class="n">gdb</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="s2">&quot;set logging redirect on&quot;</span><span class="p">)</span> + <span class="n">gdb</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="s2">&quot;set logging on&quot;</span><span class="p">)</span> + <span class="n">gdb</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="s2">&quot;maintenance info sections ?&quot;</span><span class="p">)</span> + <span class="n">gdb</span><span class="o">.</span><span class="n">flush</span><span class="p">()</span> + <span class="n">gdb</span><span class="o">.</span><span class="n">execute</span><span class="p">(</span><span class="s2">&quot;set logging off&quot;</span><span class="p">)</span> + <span class="n">output</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="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">output</span><span class="o">.</span><span class="n">splitlines</span><span class="p">():</span> + <span class="k">if</span> <span class="s1">&#39;file type&#39;</span> <span class="ow">in</span> <span class="n">line</span><span class="p">:</span> + <span class="nb">print</span><span class="p">(</span><span class="n">line</span><span class="o">.</span><span class="n">split</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">strip</span><span class="p">(</span><span class="s1">&#39;.&#39;</span><span class="p">))</span> + <span class="k">break</span> +<span class="n">Arch</span><span class="p">()</span> +</code></pre></div> + +<p>Chạy:</p> +<div class="highlight"><pre><span></span><code>$ gdb -q /bin/top +Reading symbols from /bin/top... +<span class="o">(</span>No debugging symbols found <span class="k">in</span> /bin/top<span class="o">)</span> +<span class="o">(</span>gdb<span class="o">)</span> arch +elf64-x86-64 +</code></pre></div> + +<p>Hết.</p> +<p>Thực hiện&nbsp;trên</p> +<div class="highlight"><pre><span></span><code>$ gdb --version +GNU gdb <span class="o">(</span>Ubuntu <span class="m">9</span>.2-0ubuntu1~20.04.1<span class="o">)</span> <span class="m">9</span>.2 +Copyright <span class="o">(</span>C<span class="o">)</span> <span class="m">2020</span> Free Software Foundation, Inc. +License GPLv3+: GNU GPL version <span class="m">3</span> or later &lt;http://gnu.org/licenses/gpl.html&gt; +This is free software: you are free to change and redistribute it. +There is NO WARRANTY, to the extent permitted by law. +</code></pre></div> + +<h2>Tham&nbsp;khảo</h2> +<p><a href="https://sourceware.org/gdb/current/onlinedocs/gdb.html/Extending-GDB.html#Extending-GDB">https://sourceware.org/gdb/current/onlinedocs/gdb.html/Extending-<span class="caps">GDB</span>.html#Extending-<span class="caps">GDB</span></a></p> +<h2>Liên&nbsp;quan</h2> +<ul> +<li><a href="https://familug.github.io/hoc-rust-voi-gdb.html">https://familug.github.io/hoc-rust-voi-gdb.html</a></li> +<li><a href="https://pp.pymi.vn/tag/ctf/">https://pp.pymi.vn/tag/ctf/</a></li> +<li><a href="https://n.pymi.vn/hgdb.html">bài&nbsp;trước</a></li> +</ul> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Python hello world từ debugger gdb2023-01-17T00:00:00+07:002023-01-17T00:00:00+07:00Pymier0tag:n.pymi.vn,2023-01-17:/hgdb.html<h3><span class="caps">GDB</span> là&nbsp;gì</h3> +<p><span class="caps">GDB</span> <code>GDB: The GNU Project Debugger</code> là debugger phổ biến bậc nhất thế giới, hỗ +trợ nhiều ngôn ngữ như C, Go, Rust &#8230; +Lập trình viên Python không dùng <span class="caps">GDB</span> mà dùng pdb với giao diện tương tự gdb, nhưng lập trình viên CPython (core devs) có …</p><h3><span class="caps">GDB</span> là&nbsp;gì</h3> +<p><span class="caps">GDB</span> <code>GDB: The GNU Project Debugger</code> là debugger phổ biến bậc nhất thế giới, hỗ +trợ nhiều ngôn ngữ như C, Go, Rust &#8230; +Lập trình viên Python không dùng <span class="caps">GDB</span> mà dùng pdb với giao diện tương tự gdb, nhưng lập trình viên CPython (core devs) có thể dùng tới gdb vì code CPython - viết bằng&nbsp;C.</p> +<p>Cài&nbsp;đặt</p> +<div class="highlight"><pre><span></span><code>sudo apt-get install gdb +</code></pre></div> + +<p><img alt="bug" src="https://images.unsplash.com/photo-1512887000011-f36fc9a9eeaf?ixlib=rb-4.0.3&amp;dl=krzysztof-niewolny-RVd0o9ryfAo-unsplash.jpg&amp;w=640&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb"></p> +<p>Photo by <a href="https://unsplash.com/fr/@epan5?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Krzysztof Niewolny</a> on <a href="https://unsplash.com/photos/RVd0o9ryfAo?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></p> +<p><span class="caps">GDB</span> đã có từ rất rất lâu, tuy đa năng, nhưng khá khó dùng, không &#8220;đẹp sẵn&#8221;. Khi chơi <a href="https://pp.pymi.vn/tag/ctf/"><span class="caps">CTF</span></a>, hay làm &#8220;binary exploitation&#8221;/reverse engineer, người dùng thường dùng các bản mở rộng tính năng, đẹp sãn màu mè thay gdb nguyên bản&nbsp;như:</p> +<ul> +<li><a href="https://github.com/pwndbg/pwndbg">https://github.com/pwndbg/pwndbg</a></li> +<li><a href="https://github.com/longld/peda">https://github.com/longld/peda</a></li> +<li><a href="https://github.com/hugsy/gef">https://github.com/hugsy/gef</a></li> +</ul> +<p>Trích tài liệu của <a href="https://github.com/pwndbg/pwndbg">pwndbg</a></p> +<blockquote> +<p>Many other projects from the past (e.g., gdbinit, <span class="caps">PEDA</span>) and present (e.g. <span class="caps">GEF</span>) exist to fill some these gaps. Each provides an excellent experience and great features &#8212; but they&#8217;re difficult to extend (some are unmaintained, and all are a single <span class="caps">100KB</span>, <span class="caps">200KB</span>, or <span class="caps">300KB</span> file&nbsp;(respectively)).</p> +</blockquote> +<p>Điều thú vị ở đây là cả 3 chương trình này đều viết bằng&nbsp;Python.</p> +<p>Từ bản 7 trở đi, <span class="caps">GDB</span> hỗ trợ &#8220;extending&#8221; (mở rộng) bằng các ngôn ngữ khác như Python hay guile, để kiểm tra xem bản mình cài có không&nbsp;gõ:</p> +<div class="highlight"><pre><span></span><code><span class="o">$</span><span class="w"> </span><span class="n">gdb</span><span class="w"> </span><span class="o">--</span><span class="n">configuration</span><span class="w"></span> +<span class="n">This</span><span class="w"> </span><span class="n">GDB</span><span class="w"> </span><span class="n">was</span><span class="w"> </span><span class="n">configured</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="n">follows</span><span class="p">:</span><span class="w"></span> +<span class="w"> </span><span class="n">configure</span><span class="w"> </span><span class="o">--</span><span class="n">host</span><span class="o">=</span><span class="n">x86_64</span><span class="o">-</span><span class="n">linux</span><span class="o">-</span><span class="n">gnu</span><span class="w"> </span><span class="o">--</span><span class="n">target</span><span class="o">=</span><span class="n">x86_64</span><span class="o">-</span><span class="n">linux</span><span class="o">-</span><span class="n">gnu</span><span class="w"></span> +<span class="w"> </span><span class="o">--</span><span class="n">with</span><span class="o">-</span><span class="n">auto</span><span class="o">-</span><span class="nb">load</span><span class="o">-</span><span class="n">dir</span><span class="o">=$</span><span class="n">debugdir</span><span class="p">:</span><span class="o">$</span><span class="n">datadir</span><span class="o">/</span><span class="n">auto</span><span class="o">-</span><span class="nb">load</span><span class="w"></span> +<span class="w"> </span><span class="o">--</span><span class="n">with</span><span class="o">-</span><span class="n">auto</span><span class="o">-</span><span class="nb">load</span><span class="o">-</span><span class="n">safe</span><span class="o">-</span><span class="n">path</span><span class="o">=$</span><span class="n">debugdir</span><span class="p">:</span><span class="o">$</span><span class="n">datadir</span><span class="o">/</span><span class="n">auto</span><span class="o">-</span><span class="nb">load</span><span class="w"></span> +<span class="w"> </span><span class="o">--</span><span class="n">with</span><span class="o">-</span><span class="n">expat</span><span class="w"></span> +<span class="w"> </span><span class="o">--</span><span class="n">with</span><span class="o">-</span><span class="n">gdb</span><span class="o">-</span><span class="n">datadir</span><span class="o">=/</span><span class="n">usr</span><span class="o">/</span><span class="n">share</span><span class="o">/</span><span class="n">gdb</span><span class="w"> </span><span class="p">(</span><span class="n">relocatable</span><span class="p">)</span><span class="w"></span> +<span class="w"> </span><span class="o">--</span><span class="n">with</span><span class="o">-</span><span class="n">jit</span><span class="o">-</span><span class="n">reader</span><span class="o">-</span><span class="n">dir</span><span class="o">=/</span><span class="n">usr</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">gdb</span><span class="w"> </span><span class="p">(</span><span class="n">relocatable</span><span class="p">)</span><span class="w"></span> +<span class="w"> </span><span class="o">--</span><span class="n">without</span><span class="o">-</span><span class="n">libunwind</span><span class="o">-</span><span class="n">ia64</span><span class="w"></span> +<span class="w"> </span><span class="o">--</span><span class="n">with</span><span class="o">-</span><span class="n">lzma</span><span class="w"></span> +<span class="w"> </span><span class="o">--</span><span class="n">with</span><span class="o">-</span><span class="n">babeltrace</span><span class="w"></span> +<span class="w"> </span><span class="o">--</span><span class="n">without</span><span class="o">-</span><span class="n">intel</span><span class="o">-</span><span class="n">pt</span><span class="w"></span> +<span class="w"> </span><span class="o">--</span><span class="n">with</span><span class="o">-</span><span class="n">mpfr</span><span class="w"></span> +<span class="w"> </span><span class="o">--</span><span class="n">without</span><span class="o">-</span><span class="n">xxhash</span><span class="w"></span> +<span class="w"> </span><span class="o">--</span><span class="n">with</span><span class="o">-</span><span class="n">python</span><span class="o">=/</span><span class="n">usr</span><span class="w"> </span><span class="p">(</span><span class="n">relocatable</span><span class="p">)</span><span class="w"></span> +<span class="w"> </span><span class="o">--</span><span class="n">without</span><span class="o">-</span><span class="n">guile</span><span class="w"></span> +<span class="w"> </span><span class="o">--</span><span class="n">disable</span><span class="o">-</span><span class="n">source</span><span class="o">-</span><span class="n">highlight</span><span class="w"></span> +<span class="w"> </span><span class="o">--</span><span class="n">with</span><span class="o">-</span><span class="n">separate</span><span class="o">-</span><span class="n">debug</span><span class="o">-</span><span class="n">dir</span><span class="o">=/</span><span class="n">usr</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">debug</span><span class="w"> </span><span class="p">(</span><span class="n">relocatable</span><span class="p">)</span><span class="w"></span> +<span class="w"> </span><span class="o">--</span><span class="n">with</span><span class="o">-</span><span class="n">system</span><span class="o">-</span><span class="n">gdbinit</span><span class="o">=/</span><span class="n">etc</span><span class="o">/</span><span class="n">gdb</span><span class="o">/</span><span class="n">gdbinit</span><span class="w"></span> +</code></pre></div> + +<p>bản mặc định trên Ubuntu 20.04 này có <code>--with-python</code> hỗ trợ Python và <code>--without-guile</code> không hỗ trợ&nbsp;Guile.</p> +<h3>Bật Python từ&nbsp;gdb</h3> +<p>Gõ <code>gdb -q</code> để bật gdb lên, sau đó gõ <code>pi</code> (viết tắt của python-interactive) để bật Python interpreter&nbsp;lên:</p> +<div class="highlight"><pre><span></span><code>$ gdb -q +<span class="o">(</span>gdb<span class="o">)</span> pi +&gt;&gt;&gt; sum<span class="o">(</span>i <span class="k">for</span> i <span class="k">in</span> range<span class="o">(</span><span class="m">1000</span><span class="o">)</span> <span class="k">if</span> i % <span class="nv">3</span> <span class="o">==</span> <span class="m">0</span> or i % <span class="nv">5</span> <span class="o">==</span> <span class="m">0</span><span class="o">)</span> +<span class="m">233168</span> +</code></pre></div> + +<h3>Tự viết Python&nbsp;extension</h3> +<p>gdb có 1 file &#8220;init&#8221; tại $<span class="caps">HOME</span>/.gdbinit, viết nội dung sau để gdb load code từ file khi bật lên. Ở đây ví dụ code nằm trong <code>/home/hvn/me/hgdb/hgdb.py</code>:</p> +<div class="highlight"><pre><span></span><code>source /home/hvn/me/hgdb/hgdb.py +</code></pre></div> + +<p>Trong file /home/hvn/me/hgdb/hgdb.py, viết code Python như thường, để tạo 1 command mới trong gdb, viết class kế thừa gdb.Command, chú ý sys và gdb lib được import&nbsp;sẵn:</p> +<div class="highlight"><pre><span></span><code><span class="nb">print</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">executable</span><span class="p">)</span> +<span class="nb">print</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">version</span><span class="p">)</span> +<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Hello world, from python&quot;</span><span class="p">)</span> +<span class="n">gdb</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s2">&quot;Hello world by gdb</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">)</span> + + +<span class="k">class</span> <span class="nc">HelloWorld</span><span class="p">(</span><span class="n">gdb</span><span class="o">.</span><span class="n">Command</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="nb">super</span> <span class="p">(</span><span class="n">HelloWorld</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="s2">&quot;hello&quot;</span><span class="p">,</span> <span class="n">gdb</span><span class="o">.</span><span class="n">COMMAND_USER</span><span class="p">)</span> + + + <span class="k">def</span> <span class="nf">invoke</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">arg</span><span class="p">,</span> <span class="n">from_tty</span><span class="p">):</span> + <span class="k">if</span> <span class="n">arg</span><span class="o">.</span><span class="n">strip</span><span class="p">():</span> + <span class="n">name</span> <span class="o">=</span> <span class="n">arg</span><span class="o">.</span><span class="n">strip</span><span class="p">()</span> + <span class="k">else</span><span class="p">:</span> + <span class="n">name</span> <span class="o">=</span> <span class="s2">&quot;World&quot;</span> + <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Chào, </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">!&quot;</span><span class="p">)</span> + +<span class="n">HelloWorld</span><span class="p">()</span> +</code></pre></div> + +<p>Bật gdb&nbsp;lên:</p> +<div class="highlight"><pre><span></span><code>$ gdb -q +/usr/bin/python +<span class="m">3</span>.8.10 <span class="o">(</span>default, Nov <span class="m">14</span> <span class="m">2022</span>, <span class="m">12</span>:59:47<span class="o">)</span> +<span class="o">[</span>GCC <span class="m">9</span>.4.0<span class="o">]</span> +Hello world, from python +Hello world by gdb +<span class="o">(</span>gdb<span class="o">)</span> hello +Chào, World! +<span class="o">(</span>gdb<span class="o">)</span> hello Pymier +Chào, Pymier! +</code></pre></div> + +<p>Hết.</p> +<p>Thực hiện&nbsp;trên</p> +<div class="highlight"><pre><span></span><code>$ gdb --version +GNU gdb <span class="o">(</span>Ubuntu <span class="m">9</span>.2-0ubuntu1~20.04.1<span class="o">)</span> <span class="m">9</span>.2 +Copyright <span class="o">(</span>C<span class="o">)</span> <span class="m">2020</span> Free Software Foundation, Inc. +License GPLv3+: GNU GPL version <span class="m">3</span> or later &lt;http://gnu.org/licenses/gpl.html&gt; +This is free software: you are free to change and redistribute it. +There is NO WARRANTY, to the extent permitted by law. +</code></pre></div> + +<h2>Tham&nbsp;khảo</h2> +<p><a href="https://sourceware.org/gdb/current/onlinedocs/gdb.html/Extending-GDB.html#Extending-GDB">https://sourceware.org/gdb/current/onlinedocs/gdb.html/Extending-<span class="caps">GDB</span>.html#Extending-<span class="caps">GDB</span></a></p> +<h2>Liên&nbsp;quan</h2> +<ul> +<li><a href="https://familug.github.io/hoc-rust-voi-gdb.html">https://familug.github.io/hoc-rust-voi-gdb.html</a></li> +<li><a href="https://pp.pymi.vn/tag/ctf/">https://pp.pymi.vn/tag/ctf/</a></li> +</ul> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Tổng kết cuộc thi PyMi AdventOfCode 20222022-12-26T00:00:00+07:002022-12-26T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-12-26:/aoc2022e.html<p>Suốt 25 ngày liền trong tháng 12, các code thủ đã vò tai bứt tóc tham gia chiến đấu. +Giải năm nay có tới 19 đấu thủ ghi điểm, vài đấu thủ lập bảng riêng chơi cho &#8220;đỡ ngại&#8221;???! cuối cùng, các nhà vô địch hái sao đã thuộc&nbsp;về …</p><p>Suốt 25 ngày liền trong tháng 12, các code thủ đã vò tai bứt tóc tham gia chiến đấu. +Giải năm nay có tới 19 đấu thủ ghi điểm, vài đấu thủ lập bảng riêng chơi cho &#8220;đỡ ngại&#8221;???! cuối cùng, các nhà vô địch hái sao đã thuộc&nbsp;về:</p> +<p><img alt="aoc2022" src="https://n.pymi.vn/images/aoc22.png"></p> +<ul> +<li>Giải nhất: <a href="https://github.com/tung491/aoc_2022">tung491 Python</a> - 2 thùng bia Trúc&nbsp;Bạch</li> +<li>Giải nhì: <a href="https://github.com/thaodt/aoc2022">Amidamaru Rust</a> - 3 chai bia Tiger nâu + 1 lốc 6&nbsp;budweiser.</li> +<li>Giải ba: <a href="https://github.com/htlcnn/aoc2022">Thanh Long Hoàng Python</a> - 1 chai dầu ăn Neptune + 1 lốc 6 budweiser + 1 vé massage khỏe tại Xì Gòn (hiện&nbsp;vật).</li> +</ul> +<p>Các link trên dẫn tới repo của từng code&nbsp;thủ.</p> +<p>Lễ trao giải sẽ được tổ chức tại Hà Nội vào tuần đầu tiên của năm 2023, xin mời tất cả các Pymier tham gia (địa điểm/thời gian báo sau trên kênh <a href="https://t.me/joinchat/NdnbkbTVz_44ZjA1">telegram Pymi</a>). +Tất cả các game thủ có sao sẽ nhận quà miễn phí 1 cốc bia Hà Nội + 3 chén&nbsp;rượu.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Khởi động cuộc thi PyMi AdventOfCode 2022 giải thưởng hấp dẫn2022-11-24T00:00:00+07:002022-11-24T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-11-24:/aoc2022.html<p>Đến hẹn lại lên, tháng 12 này (từ 1/12 đến 25/12), Pymi.vn sẽ tổ chức thi advent of code, một cuộc thi +code kéo dài 25 ngày. Mỗi ngày trang <a href="https://adventofcode.com/">https://adventofcode.com/</a> +sẽ đưa ra trc vấn đề vào lúc 12 giờ trưa. Ai tham gia …</p><p>Đến hẹn lại lên, tháng 12 này (từ 1/12 đến 25/12), Pymi.vn sẽ tổ chức thi advent of code, một cuộc thi +code kéo dài 25 ngày. Mỗi ngày trang <a href="https://adventofcode.com/">https://adventofcode.com/</a> +sẽ đưa ra trc vấn đề vào lúc 12 giờ trưa. Ai tham gia chỉ cần nhập mã <code>416592-56daa324</code> vào +<a href="https://adventofcode.com/2022/leaderboard/private">https://adventofcode.com/2022/leaderboard/private</a> sau khi tạo tài khoản và đăng&nbsp;nhập.</p> +<p><img alt="aoc2021" src="https://pp.pymi.vn/images/aoc21.png"> +Photo by <a href="https://unsplash.com/@dungs?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Dzung S</a> on <a href="https://unsplash.com/collections/11244300/hanoi?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></p> +<p>Khuyến khích mọi người đều tham gia, những bài đầu tiên luôn đủ dễ để làm được và độ khó tăng dần.Cuộc thi không thể hay ho gay cấn nếu không có giải thưởng. Cơ cấu&nbsp;giải:</p> +<ul> +<li>giải nhất: 1 thùng bia lon Trúc Bạch + 1 thùng trúc bạch by&nbsp;davinosoft</li> +<li>giải nhì: 3 chai bia Tiger nâu + 1 lốc 6 budweiser by&nbsp;lamdt@vccloud</li> +<li>giải ba: 1 chai dầu ăn Neptune + 1 lốc 6 budweiser by&nbsp;lamdt@vccloud</li> +</ul> +<p>thanh toán qua chuyển khoản và giá tham&nbsp;khảo.</p> +<p>Đối tượng nhận giải: mọi học viên/ cựu học viên của&nbsp;Pymi.vn</p> +<p><strong>Mong các nhà hảo tâm ủng hộ đóng góp để giải thưởng thêm to, chia cho 2 3 4 5.</strong> liên hệ kênh <a href="https://t.me/joinchat/NdnbkbTVz_44ZjA1">telegram&nbsp;Pymi</a></p> +<p><span class="caps">PS</span>: ai chưa chơi bao giờ có thể thử đề năm ngoái <a href="https://adventofcode.com/2021">https://adventofcode.com/2021</a> +Xem code python của winner 2021 <a href="https://github.com/tung491/advent_to_code_2021">https://github.com/tung491/advent_to_code_2021</a> +hay blog Pymi <a href="https://pp.pymi.vn/article/aoc2021/">https://pp.pymi.vn/article/aoc2021/</a>.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Pymi không khuyến khích học viên dùng vscode2022-11-02T00:00:00+07:002022-11-02T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-11-02:/vscode.html<p>Visual Studio Code, hay vscode, là chương trình editor mã nguồn mở của Microsoft, rất thành công và phổ biến trong giới lập trình viên, hỗ trợ rất nhiều ngôn ngữ, không to nặng như <span class="caps">IDE</span> Visual Studio, luôn nằm trong top các editor phổ biến nhất thế&nbsp;giới.</p> +<p>Gần …</p><p>Visual Studio Code, hay vscode, là chương trình editor mã nguồn mở của Microsoft, rất thành công và phổ biến trong giới lập trình viên, hỗ trợ rất nhiều ngôn ngữ, không to nặng như <span class="caps">IDE</span> Visual Studio, luôn nằm trong top các editor phổ biến nhất thế&nbsp;giới.</p> +<p>Gần đây, VSCode bắt đầu trở nên &#8220;tai tiếng&#8221; khi Microsoft mặc định bật sẵn &#8220;telemetry&#8221; để thu thập thông tin người dùng, nhưng đáng quan ngại hơn, khi những &#8220;plugin&#8221; quan trọng lại là &#8220;mã nguồn đóng&#8221;, phiên bản tắt telemetry, <a href="https://github.com/VSCodium/vscodium">VSCodium</a> không thể sử dụng Marketplace để cài plugin như VSCode (vì lý do bản quyền). NHƯNG đó không phải vấn đề của chúng tôi ở&nbsp;đây.</p> +<p><a href="https://raw.githubusercontent.com/mkrl/misbrands/master/vscode.svg"><img alt="vscode" src="https://n.pymi.vn/images/vscode.svg"></a></p> +<p>PyMi cũng từng giới thiệu vscode cho học viên với mục đích có thể dùng thứ mà đa số &#8220;mọi người đi làm&#8221; đều dùng, cài đặt đơn giản, chạy trên cả 3 hệ điều hành Windows/MacOS/Ubuntu. Cho đến khóa học PyMi 2210, khi chứng kiến tới 3 học viên nộp bài sai với những nội dung&nbsp;sau:</p> +<p><img alt="vscode" src="https://n.pymi.vn/images/vscode1.png"> +<img alt="vscode" src="https://n.pymi.vn/images/vscode2.png"> +<img alt="vscode" src="https://n.pymi.vn/images/vscode3.png"> +<img alt="vscode" src="https://n.pymi.vn/images/vscode4.png"> +<img alt="vscode" src="https://n.pymi.vn/images/vscode5.png"> +<img alt="vscode" src="https://n.pymi.vn/images/vscode7.png"></p> +<p>khi mà học viên chưa hề học import nên tất nhiên không biết nó là gì. VSCode tự tỏ ra thông minh và import lung tung vào&nbsp;giúp.</p> +<p>Giải pháp: sử dụng những editor đơn giản, không tạo sự &#8220;bất ngờ thú vị&#8221; như&nbsp;vậy.</p> +<ul> +<li>Trên cả 3 hệ điều hành có thể cài geany, sublime&nbsp;text</li> +<li>Trên Ubuntu có sẵn gedit, có thể cài&nbsp;geany.</li> +<li>Trên Windows có thể cài&nbsp;Notepad++</li> +<li>Hay vim và emacs luôn ở đó hàng 3-4 chục năm&nbsp;nay.</li> +</ul> +<p>sau này đi làm bạn hoàn toàn có thể thích dùng gì thì dùng, lúc đó có kinh nghiệm rồi thì vscode nó làm sai mình vẫn biết mà sửa (nếu mình cẩn thận, chắc&nbsp;thế?!!!).</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Python 3.10 integer lớn tùy ý, nhưng không còn in được ra màn hình2022-10-26T00:00:00+07:002022-10-26T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-10-26:/int2str.html<p>Chỉ có sự thay đổi là cố định. Đặc biệt trong ngành <span class="caps">IT</span>, sau mỗi <a href="https://en.wikipedia.org/wiki/Moore%27s_law">2 năm <span class="caps">CPU</span> &#8220;nhanh&#8221; gấp đôi</a>, công nghệ 2 3 năm lại thay đổi, thì những thứ đúng hôm qua, hôm nay càng chưa chắc còn&nbsp;đúng.</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1537949721120-e8f21f6698e6?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NjY3OTU0MTA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=600"></p> +<p>Vào bài học đầu tiên của lớp <a href="https://n.pymi.vn/pymi2210.html"><span class="caps">PYMI2210 …</span></a></p><p>Chỉ có sự thay đổi là cố định. Đặc biệt trong ngành <span class="caps">IT</span>, sau mỗi <a href="https://en.wikipedia.org/wiki/Moore%27s_law">2 năm <span class="caps">CPU</span> &#8220;nhanh&#8221; gấp đôi</a>, công nghệ 2 3 năm lại thay đổi, thì những thứ đúng hôm qua, hôm nay càng chưa chắc còn&nbsp;đúng.</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1537949721120-e8f21f6698e6?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NjY3OTU0MTA&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=600"></p> +<p>Vào bài học đầu tiên của lớp <a href="https://n.pymi.vn/pymi2210.html"><span class="caps">PYMI2210</span></a>, học viên được xõa thoải mái với integer (số nguyên), cộng trừ nhân chia thì tự dưng phép lũy thừa lại bị không thoải&nbsp;mái:</p> +<div class="highlight"><pre><span></span><code><span class="n">Python</span> <span class="mf">3.10.8</span> <span class="p">(</span><span class="n">main</span><span class="p">,</span> <span class="n">Oct</span> <span class="mi">25</span> <span class="mi">2022</span><span class="p">,</span> <span class="mi">05</span><span class="p">:</span><span class="mi">28</span><span class="p">:</span><span class="mi">56</span><span class="p">)</span> <span class="p">[</span><span class="n">GCC</span> <span class="mf">10.2.1</span> <span class="mi">20210110</span><span class="p">]</span> <span class="n">on</span> <span class="n">linux</span> +<span class="o">&gt;&gt;&gt;</span> <span class="mi">10</span><span class="o">**</span><span class="mi">4301</span> +<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span> + <span class="n">File</span> <span class="s2">&quot;&lt;stdin&gt;&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span> +<span class="ne">ValueError</span><span class="p">:</span> <span class="n">Exceeds</span> <span class="n">the</span> <span class="n">limit</span> <span class="p">(</span><span class="mi">4300</span><span class="p">)</span> <span class="k">for</span> <span class="n">integer</span> <span class="n">string</span> <span class="n">conversion</span><span class="p">;</span> <span class="n">use</span> <span class="n">sys</span><span class="o">.</span><span class="n">set_int_max_str_digits</span><span class="p">()</span> <span class="n">to</span> <span class="n">increase</span> <span class="n">the</span> <span class="n">limit</span> +</code></pre></div> + +<p>Phản ứng duy nhất của bạn chỉ có thể là &#8220;<span class="caps">WTF</span>&#8221;, sau hơn 40 khóa dạy học Python, thì đây là lần đầu tiên nhìn thấy điều này. Vậy nên nhớ rằng kiến thức cũng chỉ là nhất thời, sự thật hôm qua không thật đến ngày&nbsp;mai.</p> +<p>Đây là một thay đổi liên quan đến <a href="https://docs.python.org/3/whatsnew/3.10.html#notable-security-feature-in-3-10-7">security trong Python 3.10.7</a>, tức thậm chí Python 3.10.6 cũng chưa có, nó nằm tít dưới cùng trong doc release của&nbsp;3.10: </p> +<blockquote> +<p>Converting between int and str in bases other than 2 (binary), 4, 8 (octal), 16 (hexadecimal), or 32 such as base 10 (decimal) now raises a ValueError if the number of digits in string form is above a limit to avoid potential denial of service attacks due to the algorithmic complexity. This is a mitigation for <span class="caps">CVE</span>-2020-10735. This limit can be configured or disabled by environment variable, command line flag, or sys APIs. See the integer string conversion length limitation documentation. The default limit is 4300 digits in string&nbsp;form.</p> +</blockquote> +<p>Vì biến đổi từ int, sang string để in ra màn hình, khi số đủ lớn, có thể khiến hệ thống bị quá tải, dẫn tới có thể bị tấn công &#8220;<span class="caps">DOS</span>&#8221;. Nên từ bản 3.10.7 trở đi, số có hơn <strong>4300</strong> chữ số sẽ không được in ra màn hình nữa, muốn in phải chỉnh qua &#8220;import&nbsp;sys&#8221;.</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="nb">str</span><span class="p">(</span><span class="mi">10</span><span class="o">**</span><span class="mi">4300</span><span class="p">)</span> +<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span> + <span class="n">File</span> <span class="s2">&quot;&lt;stdin&gt;&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span> +<span class="ne">ValueError</span><span class="p">:</span> <span class="n">Exceeds</span> <span class="n">the</span> <span class="n">limit</span> <span class="p">(</span><span class="mi">4300</span><span class="p">)</span> <span class="k">for</span> <span class="n">integer</span> <span class="n">string</span> <span class="n">conversion</span><span class="p">;</span> <span class="n">use</span> <span class="n">sys</span><span class="o">.</span><span class="n">set_int_max_str_digits</span><span class="p">()</span> <span class="n">to</span> <span class="n">increase</span> <span class="n">the</span> <span class="n">limit</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">10</span><span class="o">**</span><span class="mi">4301</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">x</span> <span class="o">//</span> <span class="mi">100</span> +<span class="mf">100.</span><span class="o">...</span> +</code></pre></div> + +<p>Tính thì vẫn tính thoải mái, nhưng mặc định không đổi thành str hay in được số lớn vậy ra màn&nbsp;hình.</p> +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Lambda function có default argument2022-10-19T00:00:00+07:002022-10-19T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-10-19:/lambda_default.html<p>Từ khóa lambda dùng để tạo function 1 biểu thức trong Python, thường thấy dùng kèm +với sort, ví dụ sắp xếp giảm dần 1 list&nbsp;int:</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="nb">sorted</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="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="o">-</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">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span> +</code></pre></div> + +<p><img alt="img" src="https://images.unsplash.com/photo-1591537580018-04f735250c5f?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NjYxOTA4NjY&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=600"></p> +<p>Có thể gán function được tạo ra từ lambda vào một biến …</p><p>Từ khóa lambda dùng để tạo function 1 biểu thức trong Python, thường thấy dùng kèm +với sort, ví dụ sắp xếp giảm dần 1 list&nbsp;int:</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="nb">sorted</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="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="o">-</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">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">]</span> +</code></pre></div> + +<p><img alt="img" src="https://images.unsplash.com/photo-1591537580018-04f735250c5f?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NjYxOTA4NjY&amp;ixlib=rb-4.0.3&amp;q=80&amp;w=600"></p> +<p>Có thể gán function được tạo ra từ lambda vào một biến, ví dụ&nbsp;f:</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="n">f</span> <span class="o">=</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="mi">1</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">f</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> +<span class="mi">3</span> +<span class="o">&gt;&gt;&gt;</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="mi">1</span><span class="p">)(</span><span class="mi">2</span><span class="p">)</span> +<span class="mi">3</span> +</code></pre></div> + +<p>Lambda function cũng hỗ trợ default argument, tức nếu không đưa argument vào, +nó sẽ dùng giá trị mặc&nbsp;định:</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="p">(</span><span class="k">lambda</span> <span class="n">x</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">1</span><span class="p">)()</span> +<span class="mi">2</span> +</code></pre></div> + +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Lớp Python PyMivn khóa 42 tại Hà Nội khai giảng 18/10/20222022-10-13T00:00:00+07:002022-10-13T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-10-13:/pymi2210.html<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để trở thành lập trình viên #python chuyên&nbsp;nghiệp.</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1665602878676-219e01293b51?ixlib=rb-1.2.1&amp;dl=8machine-_-LhNDEs4MP5w-unsplash.jpg&amp;w=640&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb"></p> +<p>Photo by <a href="https://unsplash.com/@8machine_?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">8machine _</a> on <a href="https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></p> +<p>Học viên không ở Hà Nội có thể học qua livestream trên Youtube với kênh chat +Slack cùng các học viên Hà&nbsp;Nội.</p> +<p>Chi tiết tại trang chủ <a href="https://pymi.vn">Pymivn …</a></p><p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để trở thành lập trình viên #python chuyên&nbsp;nghiệp.</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1665602878676-219e01293b51?ixlib=rb-1.2.1&amp;dl=8machine-_-LhNDEs4MP5w-unsplash.jpg&amp;w=640&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb"></p> +<p>Photo by <a href="https://unsplash.com/@8machine_?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">8machine _</a> on <a href="https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a></p> +<p>Học viên không ở Hà Nội có thể học qua livestream trên Youtube với kênh chat +Slack cùng các học viên Hà&nbsp;Nội.</p> +<p>Chi tiết tại trang chủ <a href="https://pymi.vn">Pymivn</a>. +Thắc mắc/hỏi đáp: <a href="https://invite.pymi.vn">slack</a></p>Dùng C shared object .so trong CPython2022-10-12T00:00:00+07:002022-10-12T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-10-12:/so.html<p><img alt="img" src="https://images.unsplash.com/photo-1579546928686-286c9fbde1ec?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NjU1MDQ5MTU&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>CPython có thể import file C &#8220;shared library&#8221; (trên Linux là các file &#8220;shared object&#8221; có đuôi <code>.so</code> bằng việc gõ &#8220;import&nbsp;filename&#8221;.</p> +<p>Nếu có cả file <code>.so</code> và <code>.py</code> cùng tồn tại, CPython ưu tiên gọi file <code>.so</code> trước.</p> +<p>Trong stdlib của CPython, có nhiều thư viện dùng C …</p><p><img alt="img" src="https://images.unsplash.com/photo-1579546928686-286c9fbde1ec?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NjU1MDQ5MTU&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>CPython có thể import file C &#8220;shared library&#8221; (trên Linux là các file &#8220;shared object&#8221; có đuôi <code>.so</code> bằng việc gõ &#8220;import&nbsp;filename&#8221;.</p> +<p>Nếu có cả file <code>.so</code> và <code>.py</code> cùng tồn tại, CPython ưu tiên gọi file <code>.so</code> trước.</p> +<p>Trong stdlib của CPython, có nhiều thư viện dùng C như sqlite3 hay json, các thư viện này thường có 1 file .py, thực hiện nhiệm vụ import file <code>.so</code>:</p> +<div class="highlight"><pre><span></span><code><span class="err">$</span> <span class="n">find</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="mf">.8</span><span class="o">/</span> <span class="o">-</span><span class="n">name</span> <span class="s1">&#39;*sqlite*&#39;</span> +<span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="mf">.8</span><span class="o">/</span><span class="n">lib</span><span class="o">-</span><span class="n">dynload</span><span class="o">/</span><span class="n">_sqlite3</span><span class="o">.</span><span class="n">cpython</span><span class="o">-</span><span class="mi">38</span><span class="o">-</span><span class="n">x86_64</span><span class="o">-</span><span class="n">linux</span><span class="o">-</span><span class="n">gnu</span><span class="o">.</span><span class="n">so</span> +<span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="mf">.8</span><span class="o">/</span><span class="n">sqlite3</span> + +<span class="err">$</span> <span class="n">grep</span> <span class="kn">import</span> <span class="o">-</span><span class="n">R</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="mf">.8</span><span class="o">/</span><span class="n">sqlite3</span> +<span class="o">...</span> +<span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="mf">.8</span><span class="o">/</span><span class="n">sqlite3</span><span class="o">/</span><span class="n">dbapi2</span><span class="o">.</span><span class="n">py</span><span class="p">:</span><span class="kn">from</span> <span class="nn">_sqlite3</span> <span class="kn">import</span> <span class="o">*</span> +<span class="o">...</span> + +<span class="err">$</span> <span class="n">find</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="mf">.8</span><span class="o">/</span> <span class="o">-</span><span class="n">name</span> <span class="s1">&#39;*json*&#39;</span> +<span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="mf">.8</span><span class="o">/</span><span class="n">lib</span><span class="o">-</span><span class="n">dynload</span><span class="o">/</span><span class="n">_json</span><span class="o">.</span><span class="n">cpython</span><span class="o">-</span><span class="mi">38</span><span class="o">-</span><span class="n">x86_64</span><span class="o">-</span><span class="n">linux</span><span class="o">-</span><span class="n">gnu</span><span class="o">.</span><span class="n">so</span> +<span class="o">...</span> +<span class="err">$</span> <span class="n">grep</span> <span class="s1">&#39;_json&#39;</span> <span class="o">-</span><span class="n">R</span> <span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="mf">.8</span><span class="o">/</span><span class="n">json</span> +<span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="mf">.8</span><span class="o">/</span><span class="n">json</span><span class="o">/</span><span class="n">decoder</span><span class="o">.</span><span class="n">py</span><span class="p">:</span> <span class="kn">from</span> <span class="nn">_json</span> <span class="kn">import</span> <span class="n">scanstring</span> <span class="k">as</span> <span class="n">c_scanstring</span> +<span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="mf">.8</span><span class="o">/</span><span class="n">json</span><span class="o">/</span><span class="n">decoder</span><span class="o">.</span><span class="n">py</span><span class="p">:</span> <span class="c1"># Note that this exception is used from _json</span> +<span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="mf">.8</span><span class="o">/</span><span class="n">json</span><span class="o">/</span><span class="n">scanner</span><span class="o">.</span><span class="n">py</span><span class="p">:</span> <span class="kn">from</span> <span class="nn">_json</span> <span class="kn">import</span> <span class="n">make_scanner</span> <span class="k">as</span> <span class="n">c_make_scanner</span> +<span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="mf">.8</span><span class="o">/</span><span class="n">json</span><span class="o">/</span><span class="n">encoder</span><span class="o">.</span><span class="n">py</span><span class="p">:</span> <span class="kn">from</span> <span class="nn">_json</span> <span class="kn">import</span> <span class="n">encode_basestring_ascii</span> <span class="k">as</span> <span class="n">c_encode_basestring_ascii</span> +<span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="mf">.8</span><span class="o">/</span><span class="n">json</span><span class="o">/</span><span class="n">encoder</span><span class="o">.</span><span class="n">py</span><span class="p">:</span> <span class="kn">from</span> <span class="nn">_json</span> <span class="kn">import</span> <span class="n">encode_basestring</span> <span class="k">as</span> <span class="n">c_encode_basestring</span> +<span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="mf">.8</span><span class="o">/</span><span class="n">json</span><span class="o">/</span><span class="n">encoder</span><span class="o">.</span><span class="n">py</span><span class="p">:</span> <span class="kn">from</span> <span class="nn">_json</span> <span class="kn">import</span> <span class="n">make_encoder</span> <span class="k">as</span> <span class="n">c_make_encoder</span> +<span class="o">...</span> +</code></pre></div> + +<p>Xem qua 1 file&nbsp;.so:</p> +<div class="highlight"><pre><span></span><code><span class="err">$</span><span class="w"> </span><span class="k">file</span><span class="w"> </span><span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="mf">.8</span><span class="o">/</span><span class="n">lib</span><span class="o">-</span><span class="n">dynload</span><span class="o">/</span><span class="n">_sqlite3</span><span class="p">.</span><span class="n">cpython</span><span class="o">-</span><span class="mi">38</span><span class="o">-</span><span class="n">x86_64</span><span class="o">-</span><span class="n">linux</span><span class="o">-</span><span class="n">gnu</span><span class="p">.</span><span class="n">so</span><span class="w"></span> +<span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="mf">.8</span><span class="o">/</span><span class="n">lib</span><span class="o">-</span><span class="n">dynload</span><span class="o">/</span><span class="n">_sqlite3</span><span class="p">.</span><span class="n">cpython</span><span class="o">-</span><span class="mi">38</span><span class="o">-</span><span class="n">x86_64</span><span class="o">-</span><span class="n">linux</span><span class="o">-</span><span class="n">gnu</span><span class="p">.</span><span class="nl">so</span><span class="p">:</span><span class="w"> </span><span class="n">ELF</span><span class="w"> </span><span class="mi">64</span><span class="o">-</span><span class="nc">bit</span><span class="w"> </span><span class="n">LSB</span><span class="w"> </span><span class="n">shared</span><span class="w"> </span><span class="k">object</span><span class="p">,</span><span class="w"> </span><span class="n">x86</span><span class="o">-</span><span class="mi">64</span><span class="p">,</span><span class="w"> </span><span class="n">version</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">(</span><span class="n">SYSV</span><span class="p">),</span><span class="w"> </span><span class="n">dynamically</span><span class="w"> </span><span class="n">linked</span><span class="p">,</span><span class="w"> </span><span class="n">BuildID</span><span class="o">[</span><span class="n">sha1</span><span class="o">]=</span><span class="n">b65897eefdf7fd0290ce9885a91dda3781d59b59</span><span class="p">,</span><span class="w"> </span><span class="n">stripped</span><span class="w"></span> + +<span class="err">$</span><span class="w"> </span><span class="n">readelf</span><span class="w"> </span><span class="o">-</span><span class="n">a</span><span class="w"> </span><span class="o">/</span><span class="n">usr</span><span class="o">/</span><span class="n">lib</span><span class="o">/</span><span class="n">python3</span><span class="mf">.8</span><span class="o">/</span><span class="n">lib</span><span class="o">-</span><span class="n">dynload</span><span class="o">/</span><span class="n">_sqlite3</span><span class="p">.</span><span class="n">cpython</span><span class="o">-</span><span class="mi">38</span><span class="o">-</span><span class="n">x86_64</span><span class="o">-</span><span class="n">linux</span><span class="o">-</span><span class="n">gnu</span><span class="p">.</span><span class="n">so</span><span class="w"></span> +<span class="n">ELF</span><span class="w"> </span><span class="nl">Header</span><span class="p">:</span><span class="w"></span> +<span class="w"> </span><span class="nl">Magic</span><span class="p">:</span><span class="w"> </span><span class="mi">7</span><span class="n">f</span><span class="w"> </span><span class="mi">45</span><span class="w"> </span><span class="mi">4</span><span class="n">c</span><span class="w"> </span><span class="mi">46</span><span class="w"> </span><span class="mi">02</span><span class="w"> </span><span class="mi">01</span><span class="w"> </span><span class="mi">01</span><span class="w"> </span><span class="mi">00</span><span class="w"> </span><span class="mi">00</span><span class="w"> </span><span class="mi">00</span><span class="w"> </span><span class="mi">00</span><span class="w"> </span><span class="mi">00</span><span class="w"> </span><span class="mi">00</span><span class="w"> </span><span class="mi">00</span><span class="w"> </span><span class="mi">00</span><span class="w"> </span><span class="mi">00</span><span class="w"> </span> +<span class="w"> </span><span class="k">Class</span><span class="err">:</span><span class="w"> </span><span class="n">ELF64</span><span class="w"></span> +<span class="w"> </span><span class="k">Data</span><span class="err">:</span><span class="w"> </span><span class="mi">2</span><span class="err">&#39;</span><span class="n">s</span><span class="w"> </span><span class="n">complement</span><span class="p">,</span><span class="w"> </span><span class="n">little</span><span class="w"> </span><span class="n">endian</span><span class="w"></span> +<span class="w"> </span><span class="nl">Version</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="p">(</span><span class="k">current</span><span class="p">)</span><span class="w"></span> +<span class="w"> </span><span class="n">OS</span><span class="o">/</span><span class="nl">ABI</span><span class="p">:</span><span class="w"> </span><span class="n">UNIX</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="k">System</span><span class="w"> </span><span class="n">V</span><span class="w"></span> +<span class="w"> </span><span class="n">ABI</span><span class="w"> </span><span class="nl">Version</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="w"></span> +<span class="w"> </span><span class="nl">Type</span><span class="p">:</span><span class="w"> </span><span class="n">DYN</span><span class="w"> </span><span class="p">(</span><span class="n">Shared</span><span class="w"> </span><span class="k">object</span><span class="w"> </span><span class="k">file</span><span class="p">)</span><span class="w"></span> +<span class="w"> </span><span class="nl">Machine</span><span class="p">:</span><span class="w"> </span><span class="n">Advanced</span><span class="w"> </span><span class="n">Micro</span><span class="w"> </span><span class="n">Devices</span><span class="w"> </span><span class="n">X86</span><span class="o">-</span><span class="mi">64</span><span class="w"></span> +<span class="w"> </span><span class="nl">Version</span><span class="p">:</span><span class="w"> </span><span class="mh">0x1</span><span class="w"></span> +<span class="w"> </span><span class="n">Entry</span><span class="w"> </span><span class="n">point</span><span class="w"> </span><span class="nl">address</span><span class="p">:</span><span class="w"> </span><span class="mh">0x8480</span><span class="w"></span> +<span class="w"> </span><span class="k">Start</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="n">program</span><span class="w"> </span><span class="nl">headers</span><span class="p">:</span><span class="w"> </span><span class="mi">64</span><span class="w"> </span><span class="p">(</span><span class="n">bytes</span><span class="w"> </span><span class="k">into</span><span class="w"> </span><span class="k">file</span><span class="p">)</span><span class="w"></span> +<span class="w"> </span><span class="k">Start</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="k">section</span><span class="w"> </span><span class="nl">headers</span><span class="p">:</span><span class="w"> </span><span class="mi">98160</span><span class="w"> </span><span class="p">(</span><span class="n">bytes</span><span class="w"> </span><span class="k">into</span><span class="w"> </span><span class="k">file</span><span class="p">)</span><span class="w"></span> +<span class="w"> </span><span class="nl">Flags</span><span class="p">:</span><span class="w"> </span><span class="mh">0x0</span><span class="w"></span> +<span class="w"> </span><span class="k">Size</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="n">this</span><span class="w"> </span><span class="nl">header</span><span class="p">:</span><span class="w"> </span><span class="mi">64</span><span class="w"> </span><span class="p">(</span><span class="n">bytes</span><span class="p">)</span><span class="w"></span> +<span class="w"> </span><span class="k">Size</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="n">program</span><span class="w"> </span><span class="nl">headers</span><span class="p">:</span><span class="w"> </span><span class="mi">56</span><span class="w"> </span><span class="p">(</span><span class="n">bytes</span><span class="p">)</span><span class="w"></span> +<span class="w"> </span><span class="n">Number</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="n">program</span><span class="w"> </span><span class="nl">headers</span><span class="p">:</span><span class="w"> </span><span class="mi">11</span><span class="w"></span> +<span class="p">...</span><span class="w"> </span><span class="n">còn</span><span class="w"> </span><span class="n">rất</span><span class="w"> </span><span class="n">dài</span><span class="w"></span> +</code></pre></div> + +<p>Hết.</p> +<p>Bài viết thực hiện trên Ubuntu 20.04, Python&nbsp;3.8</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>zipapp - đóng gói app Python vào 1 file zip chạy được2022-09-28T00:00:00+07:002022-09-28T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-09-28:/zipapp.html<p><img alt="img" src="https://images.unsplash.com/photo-1623985153974-70b07d6a279e?ixlib=rb-1.2.1&amp;dl=fidel-fernando-fopwDMRoOlE-unsplash.jpg&amp;w=640&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb"></p> +<p>Code Python có thể được đóng trong 1 file zip và chạy được (như file .exe trên Windows). Ví dụ đơn giản trên&nbsp;Ubuntu:</p> +<div class="highlight"><pre><span></span><code>$ mkdir ziptest +$ <span class="nb">echo</span> <span class="s1">&#39;print(&quot;Hello Pymier&quot;)&#39;</span> &gt; ziptest/__main__.py +$ python3 -m zipapp ziptest +$ ls -l ziptest.pyz +-rw-rw-r-- <span class="m">1</span> hvn hvn <span class="m">142</span> Sep <span class="m">28</span> <span class="m">21</span>:24 …</code></pre></div><p><img alt="img" src="https://images.unsplash.com/photo-1623985153974-70b07d6a279e?ixlib=rb-1.2.1&amp;dl=fidel-fernando-fopwDMRoOlE-unsplash.jpg&amp;w=640&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb"></p> +<p>Code Python có thể được đóng trong 1 file zip và chạy được (như file .exe trên Windows). Ví dụ đơn giản trên&nbsp;Ubuntu:</p> +<div class="highlight"><pre><span></span><code>$ mkdir ziptest +$ <span class="nb">echo</span> <span class="s1">&#39;print(&quot;Hello Pymier&quot;)&#39;</span> &gt; ziptest/__main__.py +$ python3 -m zipapp ziptest +$ ls -l ziptest.pyz +-rw-rw-r-- <span class="m">1</span> hvn hvn <span class="m">142</span> Sep <span class="m">28</span> <span class="m">21</span>:24 ziptest.pyz +$ file ziptest.pyz +ziptest.pyz: Zip archive data, at least v2.0 to extract +$ zipinfo ziptest.pyz +Archive: ziptest.pyz +Zip file size: <span class="m">142</span> bytes, number of entries: <span class="m">1</span> +-rw-rw-r-- <span class="m">2</span>.0 unx <span class="m">22</span> b- stor <span class="m">22</span>-Sep-28 <span class="m">21</span>:20 __main__.py +<span class="m">1</span> file, <span class="m">22</span> bytes uncompressed, <span class="m">22</span> bytes compressed: <span class="m">0</span>.0% +$ python ziptest.pyz +Hello Pymier +</code></pre></div> + +<p>Code trong file <code>__main__.py</code> sẽ được&nbsp;chạy.</p> +<h3>Chỉ là 1 file&nbsp;zip</h3> +<p>Việc tạo file zip này không cần bất cứ tool gì đặt biệt, chỉ cần tạo 1 file zip chứa file <code>__main__.py</code> (và N file khác nếu cần) là&nbsp;được.</p> +<p>Tài liệu của Python lib zipapp có giải thích format này, chú ý rằng file zip có header ở chỗ nào cũng được (chứ không bắt buộc phải ở đầu&nbsp;file):</p> +<p>The zip file format allows arbitrary data to be prepended to a zip file. The zip application format uses this ability to prepend a standard <span class="caps">POSIX</span> “shebang” line to the file (#!/path/to/interpreter). +Formally, the Python zip application format is&nbsp;therefore:</p> +<div class="highlight"><pre><span></span><code><span class="nv">An</span><span class="w"> </span><span class="nv">optional</span><span class="w"> </span><span class="nv">shebang</span><span class="w"> </span><span class="nv">line</span>,<span class="w"> </span><span class="nv">containing</span><span class="w"> </span><span class="nv">the</span><span class="w"> </span><span class="nv">characters</span><span class="w"> </span><span class="nv">b</span><span class="s1">&#39;#!&#39;</span><span class="w"> </span><span class="nv">followed</span><span class="w"> </span><span class="nv">by</span><span class="w"> </span><span class="nv">an</span><span class="w"> </span><span class="nv">interpreter</span><span class="w"> </span><span class="nv">name</span>,<span class="w"> </span><span class="nv">and</span><span class="w"> </span><span class="k">then</span><span class="w"> </span><span class="nv">a</span><span class="w"> </span><span class="nv">newline</span><span class="w"> </span><span class="ss">(</span><span class="nv">b</span><span class="s1">&#39;\n&#39;</span><span class="ss">)</span><span class="w"> </span><span class="nv">character</span>.<span class="w"> </span><span class="nv">The</span><span class="w"> </span><span class="nv">interpreter</span><span class="w"> </span><span class="nv">name</span><span class="w"> </span><span class="nv">can</span><span class="w"> </span><span class="nv">be</span><span class="w"> </span><span class="nv">anything</span><span class="w"> </span><span class="nv">acceptable</span><span class="w"> </span><span class="nv">to</span><span class="w"> </span><span class="nv">the</span><span class="w"> </span><span class="nv">OS</span><span class="w"> </span>“<span class="nv">shebang</span>”<span class="w"> </span><span class="nv">processing</span>,<span class="w"> </span><span class="nv">or</span><span class="w"> </span><span class="nv">the</span><span class="w"> </span><span class="nv">Python</span><span class="w"> </span><span class="nv">launcher</span><span class="w"> </span><span class="nv">on</span><span class="w"> </span><span class="nv">Windows</span>.<span class="w"> </span><span class="nv">The</span><span class="w"> </span><span class="nv">interpreter</span><span class="w"> </span><span class="nv">should</span><span class="w"> </span><span class="nv">be</span><span class="w"> </span><span class="nv">encoded</span><span class="w"> </span><span class="nv">in</span><span class="w"> </span><span class="nv">UTF</span><span class="o">-</span><span class="mi">8</span><span class="w"> </span><span class="nv">on</span><span class="w"> </span><span class="nv">Windows</span>,<span class="w"> </span><span class="nv">and</span><span class="w"> </span><span class="nv">in</span><span class="w"> </span><span class="nv">sys</span>.<span class="nv">getfilesystemencoding</span><span class="ss">()</span><span class="w"> </span><span class="nv">on</span><span class="w"> </span><span class="nv">POSIX</span>.<span class="w"></span> + +<span class="nv">Standard</span><span class="w"> </span><span class="nv">zipfile</span><span class="w"> </span><span class="nv">data</span>,<span class="w"> </span><span class="nv">as</span><span class="w"> </span><span class="nv">generated</span><span class="w"> </span><span class="nv">by</span><span class="w"> </span><span class="nv">the</span><span class="w"> </span><span class="nv">zipfile</span><span class="w"> </span><span class="nv">module</span>.<span class="w"> </span><span class="nv">The</span><span class="w"> </span><span class="nv">zipfile</span><span class="w"> </span><span class="nv">content</span><span class="w"> </span><span class="nv">must</span><span class="w"> </span><span class="k">include</span><span class="w"> </span><span class="nv">a</span><span class="w"> </span><span class="nv">file</span><span class="w"> </span><span class="nv">called</span><span class="w"> </span><span class="nv">__main__</span>.<span class="nv">py</span><span class="w"> </span><span class="ss">(</span><span class="nv">which</span><span class="w"> </span><span class="nv">must</span><span class="w"> </span><span class="nv">be</span><span class="w"> </span><span class="nv">in</span><span class="w"> </span><span class="nv">the</span><span class="w"> </span>“<span class="nv">root</span>”<span class="w"> </span><span class="nv">of</span><span class="w"> </span><span class="nv">the</span><span class="w"> </span><span class="nv">zipfile</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="nv">i</span>.<span class="nv">e</span>.,<span class="w"> </span><span class="nv">it</span><span class="w"> </span><span class="nv">cannot</span><span class="w"> </span><span class="nv">be</span><span class="w"> </span><span class="nv">in</span><span class="w"> </span><span class="nv">a</span><span class="w"> </span><span class="nv">subdirectory</span><span class="ss">)</span>.<span class="w"> </span><span class="nv">The</span><span class="w"> </span><span class="nv">zipfile</span><span class="w"> </span><span class="nv">data</span><span class="w"> </span><span class="nv">can</span><span class="w"> </span><span class="nv">be</span><span class="w"> </span><span class="nv">compressed</span><span class="w"> </span><span class="nv">or</span><span class="w"> </span><span class="nv">uncompressed</span>.<span class="w"></span> +</code></pre></div> + +<p>If an application archive has a shebang line, it may have the executable bit set on <span class="caps">POSIX</span> systems, to allow it to be executed&nbsp;directly.</p> +<p>There is no requirement that the tools in this module are used to create application archives - the module is a convenience, but archives in the above format created by any means are acceptable to&nbsp;Python.</p> +<p><a href="https://docs.python.org/3/library/zipapp.html">https://docs.python.org/3/library/zipapp.html</a></p> +<h3>Làm một app dùng lib requests,&nbsp;beautifulsoup4</h3> +<p>Tạo thư mục myapp, +tạo file <code>myapp/__main__.py</code> với nội&nbsp;dung</p> +<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">bs4</span> <span class="kn">import</span> <span class="n">BeautifulSoup</span> +<span class="kn">import</span> <span class="nn">requests</span> +<span class="n">resp</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;https://pymi.vn/&quot;</span><span class="p">)</span> +<span class="n">tree</span> <span class="o">=</span> <span class="n">BeautifulSoup</span><span class="p">(</span><span class="n">resp</span><span class="o">.</span><span class="n">text</span><span class="p">,</span> <span class="n">features</span><span class="o">=</span><span class="s2">&quot;lxml&quot;</span><span class="p">)</span> +<span class="n">i_tags</span> <span class="o">=</span> <span class="p">[</span> + <span class="n">node</span><span class="o">.</span><span class="n">text</span> + <span class="k">for</span> <span class="n">node</span> <span class="ow">in</span> <span class="n">tree</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="s2">&quot;div&quot;</span><span class="p">,</span> <span class="n">attrs</span><span class="o">=</span><span class="p">{</span><span class="s2">&quot;role&quot;</span><span class="p">:</span> <span class="s2">&quot;alert&quot;</span><span class="p">})</span><span class="o">.</span><span class="n">find_all</span><span class="p">(</span> + <span class="s2">&quot;li&quot;</span> + <span class="p">)</span> +<span class="p">]</span> +<span class="k">for</span> <span class="n">msg</span> <span class="ow">in</span> <span class="n">i_tags</span><span class="p">:</span> + <span class="nb">print</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span> +</code></pre></div> + +<p>Chạy lệnh để cài các thư viện và đóng gói&nbsp;zipapp:</p> +<div class="highlight"><pre><span></span><code><span class="err">$</span> <span class="n">python3</span> <span class="o">-</span><span class="n">m</span> <span class="n">pip</span> <span class="n">install</span> <span class="n">requests</span> <span class="n">bs4</span> <span class="o">--</span><span class="n">target</span> <span class="n">myapp</span> +<span class="o">...</span> +<span class="err">$</span> <span class="n">ls</span> <span class="n">myapp</span><span class="o">/</span> +<span class="n">beautifulsoup4</span><span class="o">-</span><span class="mf">4.11.1</span><span class="o">.</span><span class="n">dist</span><span class="o">-</span><span class="n">info</span> <span class="n">idna</span> +<span class="nb">bin</span> <span class="n">idna</span><span class="o">-</span><span class="mf">3.4</span><span class="o">.</span><span class="n">dist</span><span class="o">-</span><span class="n">info</span> +<span class="n">bs4</span> <span class="n">requests</span> +<span class="n">bs4</span><span class="o">-</span><span class="mf">0.0.1</span><span class="o">.</span><span class="n">egg</span><span class="o">-</span><span class="n">info</span> <span class="n">requests</span><span class="o">-</span><span class="mf">2.28.1</span><span class="o">.</span><span class="n">dist</span><span class="o">-</span><span class="n">info</span> +<span class="n">certifi</span> <span class="n">soupsieve</span> +<span class="n">certifi</span><span class="o">-</span><span class="mf">2022.9.24</span><span class="o">.</span><span class="n">dist</span><span class="o">-</span><span class="n">info</span> <span class="n">soupsieve</span><span class="o">-</span><span class="mf">2.3.2</span><span class="o">.</span><span class="n">post1</span><span class="o">.</span><span class="n">dist</span><span class="o">-</span><span class="n">info</span> +<span class="n">charset_normalizer</span> <span class="n">urllib3</span> +<span class="n">charset_normalizer</span><span class="o">-</span><span class="mf">2.1.1</span><span class="o">.</span><span class="n">dist</span><span class="o">-</span><span class="n">info</span> <span class="n">urllib3</span><span class="o">-</span><span class="mf">1.26.12</span><span class="o">.</span><span class="n">dist</span><span class="o">-</span><span class="n">info</span> +<span class="err">$</span> <span class="n">python3</span> <span class="o">-</span><span class="n">m</span> <span class="n">zipapp</span> <span class="o">-</span><span class="n">p</span> <span class="s2">&quot;/usr/bin/env python3&quot;</span> <span class="n">myapp</span> +<span class="err">$</span> <span class="o">./</span><span class="n">myapp</span><span class="o">.</span><span class="n">pyz</span> +<span class="n">TPHCM</span> <span class="err">😱</span> <span class="n">Dự</span> <span class="n">kiến</span> <span class="n">khai</span> <span class="n">giảng</span> <span class="n">học</span> <span class="n">livestream</span> <span class="n">với</span> <span class="n">lớp</span> <span class="n">Hà</span> <span class="n">Nội</span> <span class="n">giữa</span> <span class="n">tháng</span> <span class="mi">10</span> <span class="mi">2022</span> <span class="err">🎉</span><span class="o">.</span> +<span class="n">Hà</span> <span class="n">Nội</span> <span class="err">😱</span> <span class="n">Dự</span> <span class="n">kiến</span> <span class="n">khai</span> <span class="n">giảng</span> <span class="n">giữa</span> <span class="n">tháng</span> <span class="mi">10</span> <span class="mi">2022</span> <span class="err">🎉</span><span class="o">.</span> +</code></pre></div> + +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>totp là gì? quét mã qr để thêm totp là làm gì?2022-09-27T00:00:00+07:002022-09-27T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-09-27:/totp.html<p><img alt="img" src="https://freeotp.github.io/img/android.png"></p> +<p>Ngày nay, khi có đủ chiêu trò để lừa người dùng lấy password, mọi tài khỏan &#8220;nghiêm túc&#8221; đều cần có xác thực <span class="caps">2FA</span> (2 factors authen) tức cung cấp thêm 1 bí mật khác kèm password. +<span class="caps">2FA</span> &#8220;lạc hậu&#8221; gửi <span class="caps">OTP</span> qua tin nhắn, mặc dù việc hack sóng …</p><p><img alt="img" src="https://freeotp.github.io/img/android.png"></p> +<p>Ngày nay, khi có đủ chiêu trò để lừa người dùng lấy password, mọi tài khỏan &#8220;nghiêm túc&#8221; đều cần có xác thực <span class="caps">2FA</span> (2 factors authen) tức cung cấp thêm 1 bí mật khác kèm password. +<span class="caps">2FA</span> &#8220;lạc hậu&#8221; gửi <span class="caps">OTP</span> qua tin nhắn, mặc dù việc hack sóng nhà mạng không quá phổ biến, nhưng hòan toàn khả thi khi tấn công có tổ chức. +<span class="caps">2FA</span> hiện đại, thay vì tin nhắn, sử dụng 1 phần mềm sinh mã 6-8 số mỗi 30s, được các tập đoàn công nghệ hàng đầu như Google, Facebook, GitHub, CloudFlare, GitLab&#8230; tin dùng. +Hay có chỗ dùng phần cứng chuyên dụng sinh mã <span class="caps">OTP</span>.</p> +<h3>Các phần mềm sinh <span class="caps">TOTP</span></h3> +<p>Nhiều phần mềm free, open source có cung cấp khả năng tương đương&nbsp;như:</p> +<ul> +<li>[mobile] FreeOTP của <a href="https://freeotp.github.io/">RedHat</a></li> +<li><a href="https://keepassxc.org/docs/KeePassXC_UserGuide.html#_adding_totp_to_an_entry">KeepassXC</a></li> +<li><a href="https://github.com/tadeck/onetimepass">Python lib&nbsp;onetimepass</a></li> +<li>&#8230;</li> +</ul> +<p>hoặc closed&nbsp;source:</p> +<ul> +<li>Google&nbsp;Authenticator</li> +<li>1Password</li> +<li>Authy</li> +<li>LastPass&nbsp;Authenticator</li> +<li>Microsoft&nbsp;Authenticator.</li> +</ul> +<h3>Đăng ký <span class="caps">TOTP</span> (Timed One-Time Passwords) là&nbsp;gì?</h3> +<p>mã <span class="caps">TOTP</span> được sinh ra theo thuật toán mô tả ờ <a href="https://www.ietf.org/rfc/rfc6238.txt"><span class="caps">RFC6238</span></a> từ một mã bí mật (secret) và thời&nbsp;gian.</p> +<p>Khi quét mã <span class="caps">QR</span> để thêm một <span class="caps">OTP</span> vào app, thông tin mã <span class="caps">QR</span> chứa secret là một đoạn text đã encode <a href="https://n.pymi.vn/base64.html">base32 (A-V0-9)</a>. Ví du: <code>otpauth://totp/alice@google.com?secret=JBSWY3DPEHPK3PXP</code></p> +<h3>Backup <span class="caps">TOTP</span></h3> +<p>Copy mã secret. Với phần mềm như FreeOTP, cần truy cập vào file trên điện thoại để lấy&nbsp;secret.</p> +<p>Hết.</p> +<p>Bonus:</p> +<ul> +<li><span class="caps">TOTP</span> app trong 20 dòng Python <a href="https://github.com/susam/mintotp">mintotp</a></li> +</ul> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>mypy là chưa đủ, cần phải nghiêm khắc hơn2022-09-17T00:00:00+07:002022-09-17T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-09-17:/mypystrict.html<p><img alt="img" src="https://images.unsplash.com/photo-1663312314645-555d6bb36435?ixlib=rb-1.2.1&amp;dl=single-earth-AxkQNRFzIow-unsplash.jpg&amp;w=640&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb"></p> +<p><a href="https://pp.pymi.vn/article/mypy/">mypy</a> là công cụ không thể thiếu khi code Python ở thập niên thứ 2 thế kỷ 21. +<a href="https://pp.pymi.vn/article/mypy/">mypy</a> giúp phát hiện hàng loạt lỗi liên quan đến type, mang tới sức mạnh của static typing language tới Python. Nhưng đôi khi, là chưa&nbsp;đủ.</p> +<p>Thử sức xem bạn có …</p><p><img alt="img" src="https://images.unsplash.com/photo-1663312314645-555d6bb36435?ixlib=rb-1.2.1&amp;dl=single-earth-AxkQNRFzIow-unsplash.jpg&amp;w=640&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb"></p> +<p><a href="https://pp.pymi.vn/article/mypy/">mypy</a> là công cụ không thể thiếu khi code Python ở thập niên thứ 2 thế kỷ 21. +<a href="https://pp.pymi.vn/article/mypy/">mypy</a> giúp phát hiện hàng loạt lỗi liên quan đến type, mang tới sức mạnh của static typing language tới Python. Nhưng đôi khi, là chưa&nbsp;đủ.</p> +<p>Thử sức xem bạn có phát hiện ra lỗi trong đoạn code này? Tác giả muốn bật <span class="caps">DEBUG</span> (django) khi dev trên&nbsp;MacOS:</p> +<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">platform</span> + +<span class="k">if</span> <span class="n">platform</span><span class="o">.</span><span class="n">system</span> <span class="o">==</span> <span class="s2">&quot;Darwin&quot;</span><span class="p">:</span> + <span class="n">DEBUG</span> <span class="o">=</span> <span class="kc">True</span> + +<span class="k">if</span> <span class="n">platform</span><span class="o">.</span><span class="n">system</span> <span class="o">+</span> <span class="s2">&quot;Darwin&quot;</span><span class="p">:</span> + <span class="n">DEBUG</span> <span class="o">=</span> <span class="kc">True</span> +</code></pre></div> + +<p>mypy phát hiện ra lỗi khi thực hiện phép <code>+</code> ở câu <code>if</code> thứ&nbsp;hai</p> +<div class="highlight"><pre><span></span><code>$ mypy testtype.py +testtype.py:6: error: Unsupported left operand <span class="nb">type</span> <span class="k">for</span> + <span class="o">(</span><span class="s2">&quot;Callable[[], str]&quot;</span><span class="o">)</span> +Found <span class="m">1</span> error <span class="k">in</span> <span class="m">1</span> file <span class="o">(</span>checked <span class="m">1</span> <span class="nb">source</span> file<span class="o">)</span> +</code></pre></div> + +<p>nhưng không phát hiện ra vấn đề ở câu <code>if</code> thứ nhất, vì đó là việc làm hoàn toàn hợp lệ trong Python: so sánh hai giá trị khác kiểu. +Tác giả viết thiếu dấu <code>()</code>, code sau khi cào đầu bứt&nbsp;tóc:</p> +<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">platform</span> + +<span class="k">if</span> <span class="n">platform</span><span class="o">.</span><span class="n">system</span><span class="p">()</span> <span class="o">==</span> <span class="s2">&quot;Darwin&quot;</span><span class="p">:</span> + <span class="n">DEBUG</span> <span class="o">=</span> <span class="kc">True</span> +</code></pre></div> + +<p>Thực chất, mypy cũng có thể phát hiện ra vấn đề này, nhưng phải thêm option&nbsp;strict:</p> +<div class="highlight"><pre><span></span><code>$ mypy --strict testtype.py +testtype.py:3: error: Non-overlapping equality check <span class="o">(</span>left operand type: <span class="s2">&quot;Callable[[], str]&quot;</span>, right operand type: <span class="s2">&quot;Literal[&#39;Darwin&#39;]&quot;</span><span class="o">)</span> +testtype.py:6: error: Unsupported left operand <span class="nb">type</span> <span class="k">for</span> + <span class="o">(</span><span class="s2">&quot;Callable[[], str]&quot;</span><span class="o">)</span> +Found <span class="m">2</span> errors <span class="k">in</span> <span class="m">1</span> file <span class="o">(</span>checked <span class="m">1</span> <span class="nb">source</span> file<span class="o">)</span> +</code></pre></div> + +<h3>Kết&nbsp;luận</h3> +<p>Bật &#8212;strict&nbsp;lên.</p> +<p>Bài viết thực hiện&nbsp;với</p> +<div class="highlight"><pre><span></span><code>$ python3 --version +Python <span class="m">3</span>.8.10 + +$ mypy --version +mypy <span class="m">0</span>.942 +</code></pre></div> + +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Dùng z3 giải hệ phương trình số lớn giải mã rsa2022-08-29T00:00:00+07:002022-08-29T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-08-29:/z3factor.html<p><img alt="img" src="https://images.unsplash.com/photo-1661621768920-5b5cdafc96c4?ixlib=rb-1.2.1&amp;dl=dibakar-roy-JAvgd-akITY-unsplash.jpg&amp;w=640&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb"></p> +<p><a href="https://ctftime.org/event/1676">MapleCTF 2022</a> ra đề Brsaby Crypto với nội&nbsp;dung:</p> +<ul> +<li>Cho p * q ==&nbsp;N</li> +<li>Cho <code>p**4 - q**3 == hint</code></li> +<li>Tìm p và q rồi giải mã lấy&nbsp;flag.</li> +</ul> +<h3><span class="caps">RSA</span> trong 2&nbsp;phút</h3> +<p>Đây là 1 phần trong bài tóan giải mã <span class="caps">RSA</span>. <a href="https://en.wikipedia.org/wiki/RSA_(algorithm)"><span class="caps">RSA</span> là hệ mã hóa phổ biến nhất …</a></p><p><img alt="img" src="https://images.unsplash.com/photo-1661621768920-5b5cdafc96c4?ixlib=rb-1.2.1&amp;dl=dibakar-roy-JAvgd-akITY-unsplash.jpg&amp;w=640&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb"></p> +<p><a href="https://ctftime.org/event/1676">MapleCTF 2022</a> ra đề Brsaby Crypto với nội&nbsp;dung:</p> +<ul> +<li>Cho p * q ==&nbsp;N</li> +<li>Cho <code>p**4 - q**3 == hint</code></li> +<li>Tìm p và q rồi giải mã lấy&nbsp;flag.</li> +</ul> +<h3><span class="caps">RSA</span> trong 2&nbsp;phút</h3> +<p>Đây là 1 phần trong bài tóan giải mã <span class="caps">RSA</span>. <a href="https://en.wikipedia.org/wiki/RSA_(algorithm)"><span class="caps">RSA</span> là hệ mã hóa phổ biến nhất hiện</a> +tại, được dùng ở hầu hết chỗ nào cần mã hóa trên máy tính. +Nền tảng của <span class="caps">RSA</span> dựa trên một bài toán khó &#8220;gần như&#8221; không giải được dễ dàng: +tìm 2 số nhân với nhau ra một số N rất lớn. +Sau đó, mã hóa một bí mật m bằng công&nbsp;thức:</p> +<ul> +<li><code>c = m**e % N</code></li> +</ul> +<p>với e cho công khai cùng N, thường có giá trị&nbsp;65535.</p> +<p>Để giải mã, cần tim ra 2 số đã nhân thành N, gọi là p, q. +Từ p,q tính ra&nbsp;d:</p> +<ul> +<li><code>phi =(p-1)*(q-1)</code></li> +<li><code>d=(e**-1 % phi)</code></li> +</ul> +<p>rồi giải mã bằng công thức <code>c**d mod N</code>.</p> +<h3>Factorize N tìm p,q bằng&nbsp;z3</h3> +<p>Việc tìm các ước p,q của số N gọi là&nbsp;factorize.</p> +<p>Bài&nbsp;giải:</p> +<div class="highlight"><pre><span></span><code><span class="c1"># pip install pycryptodome==3.14.1</span> +<span class="kn">from</span> <span class="nn">Crypto.Util.number</span> <span class="kn">import</span> <span class="n">getPrime</span><span class="p">,</span> <span class="n">bytes_to_long</span><span class="p">,</span> <span class="n">long_to_bytes</span> +<span class="c1">#from secret import FLAG</span> +<span class="c1">#msg = bytes_to_long(FLAG)</span> +<span class="c1">#p = getPrime(512)</span> +<span class="c1">#q = getPrime(512)</span> +<span class="c1">#N = p*q</span> +<span class="c1"># e = 0x10001</span> +<span class="c1"># enc = pow(msg, e, N)</span> +<span class="c1"># hint = p**4 - q**3</span> +<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">N</span> <span class="si">= }</span><span class="s2">&quot;</span><span class="p">)</span> +<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">e</span> <span class="si">= }</span><span class="s2">&quot;</span><span class="p">)</span> +<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">enc</span> <span class="si">= }</span><span class="s2">&quot;</span><span class="p">)</span> +<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="n">hint</span> <span class="si">= }</span><span class="s2">&quot;</span><span class="p">)</span> + +<span class="n">N</span> <span class="o">=</span> <span class="mi">134049493752540418773065530143076126635445393203564220282068096099004424462500237164471467694656029850418188898633676218589793310992660499303428013844428562884017060683631593831476483842609871002334562252352992475614866865974358629573630911411844296034168928705543095499675521713617474013653359243644060206273</span> +<span class="n">e</span> <span class="o">=</span> <span class="mi">65537</span> +<span class="n">enc</span> <span class="o">=</span> <span class="mi">110102068225857249266317472106969433365215711224747391469423595211113736904624336819727052620230568210114877696850912188601083627767033947343144894754967713943008865252845680364312307500261885582194931443807130970738278351511194280306132200450370953028936210150584164591049215506801271155664701637982648648103</span> +<span class="n">hint</span> <span class="o">=</span> <span class="mi">20172108941900018394284473561352944005622395962339433571299361593905788672168045532232800087202397752219344139121724243795336720758440190310585711170413893436453612554118877290447992615675653923905848685604450760355869000618609981902108252359560311702189784994512308860998406787788757988995958832480986292341328962694760728098818022660328680140765730787944534645101122046301434298592063643437213380371824613660631584008711686240103416385845390125711005079231226631612790119628517438076962856020578250598417110996970171029663545716229258911304933901864735285384197017662727621049720992964441567484821110407612560423282</span> + +<span class="c1">## giải</span> +<span class="kn">from</span> <span class="nn">z3</span> <span class="kn">import</span> <span class="n">Solver</span><span class="p">,</span> <span class="n">Ints</span> +<span class="n">p</span><span class="p">,</span> <span class="n">q</span> <span class="o">=</span> <span class="n">Ints</span><span class="p">(</span><span class="s1">&#39;p q&#39;</span><span class="p">)</span> +<span class="n">s</span> <span class="o">=</span> <span class="n">Solver</span><span class="p">()</span> + +<span class="n">s</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">p</span><span class="o">**</span><span class="mi">4</span><span class="o">-</span><span class="n">q</span><span class="o">**</span><span class="mi">3</span><span class="o">==</span><span class="n">hint</span><span class="p">)</span> +<span class="n">s</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">p</span> <span class="o">*</span> <span class="n">q</span> <span class="o">==</span> <span class="n">N</span><span class="p">)</span> +<span class="n">s</span><span class="o">.</span><span class="n">check</span><span class="p">()</span> +<span class="n">m</span> <span class="o">=</span> <span class="p">(</span><span class="n">s</span><span class="o">.</span><span class="n">model</span><span class="p">())</span> +<span class="n">p</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">m</span><span class="p">[</span><span class="n">p</span><span class="p">]))</span> +<span class="n">q</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">m</span><span class="p">[</span><span class="n">q</span><span class="p">]))</span> +<span class="k">assert</span> <span class="n">p</span><span class="o">*</span><span class="n">q</span> <span class="o">==</span> <span class="n">N</span> +<span class="n">phi</span> <span class="o">=</span> <span class="p">(</span><span class="n">p</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span><span class="o">*</span><span class="p">(</span><span class="n">q</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span> +<span class="n">d</span> <span class="o">=</span> <span class="nb">pow</span><span class="p">(</span><span class="n">e</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">phi</span><span class="p">)</span> +<span class="n">flag</span><span class="o">=</span> <span class="nb">pow</span><span class="p">(</span><span class="n">enc</span><span class="p">,</span> <span class="n">d</span><span class="p">,</span> <span class="n">N</span><span class="p">)</span> +<span class="nb">print</span><span class="p">(</span><span class="n">long_to_bytes</span><span class="p">(</span><span class="n">flag</span><span class="p">))</span> +</code></pre></div> + +<p>Ta thu được flag <code>b'maple{s0lving_th3m_p3rf3ct_r000ts_1s_fun}</code></p> +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Python leak var2022-08-10T00:00:00+07:002022-08-10T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-08-10:/leakvar.html<p><img alt="img" src="https://images.unsplash.com/photo-1645220305088-85956fc0d482?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NjAxNDAwODM&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Python có một &#8220;tính năng&#8221; rất bất ngờ nếu bạn đã lập trình các ngôn ngữ&nbsp;khác:</p> +<div class="highlight"><pre><span></span><code><span class="n">i</span> <span class="o">=</span> <span class="s2">&quot;Bánh Trung Thu&quot;</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">5</span><span class="p">):</span> + <span class="nb">print</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> + +<span class="nb">print</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="c1"># cái này hiện ra gì?</span> +</code></pre></div> + +<p>tất cả các i trong đoạn code trên đều là 1 i, đầu tiên nó …</p><p><img alt="img" src="https://images.unsplash.com/photo-1645220305088-85956fc0d482?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NjAxNDAwODM&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Python có một &#8220;tính năng&#8221; rất bất ngờ nếu bạn đã lập trình các ngôn ngữ&nbsp;khác:</p> +<div class="highlight"><pre><span></span><code><span class="n">i</span> <span class="o">=</span> <span class="s2">&quot;Bánh Trung Thu&quot;</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">5</span><span class="p">):</span> + <span class="nb">print</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> + +<span class="nb">print</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="c1"># cái này hiện ra gì?</span> +</code></pre></div> + +<p>tất cả các i trong đoạn code trên đều là 1 i, đầu tiên nó là string, sau đó được gán cho các số 0 tới 4, sau vòng for nó&#8230; giữ giá trị cuối cùng:&nbsp;4</p> +<p>Tính năng này không có ở hầu hết các ngôn ngữ lập trình khác, và thậm chí với Python, nó cũng là 1 bug, nhưng vì đã tồn tại quá lâu và không thể sửa mà không ảnh hưởng tới các đoạn code đã tồn tại, nên người ta giữ nó, coi như 1 tính&nbsp;năng.</p> +<p>Tính năng này sẽ có ngày khiến bạn vò đầu bứt tai, càng dễ gặp khi làm đúng các best practice đặt tên có nghĩa. Ví&nbsp;dụ:</p> +<div class="highlight"><pre><span></span><code><span class="n">name</span> <span class="o">=</span> <span class="s2">&quot;ABC&quot;</span> +<span class="n">send_to_slack</span> <span class="o">=</span> <span class="kc">True</span> +<span class="n">send_to_telegram</span> <span class="o">=</span> <span class="kc">True</span> +<span class="k">if</span> <span class="n">send_to_slack</span><span class="p">:</span> + <span class="o">...</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">send_message_to</span><span class="p">(</span><span class="n">name</span><span class="p">)</span> + +<span class="o">...</span> +<span class="c1">#10 dòng sau</span> +<span class="o">...</span> +<span class="o">...</span> + +<span class="k">if</span> <span class="n">send_to_telegram</span><span class="p">:</span> + <span class="o">...</span> + <span class="n">do_something_with_name</span><span class="p">(</span><span class="n">name</span><span class="p">)</span> +</code></pre></div> + +<p>Việc dùng <code>name</code> trong vòng lặp for vô tình đã thay mất giá trị của <code>name</code> đã +có trước, và chỉ ảnh hưởng tới câu if số 2 nếu câu if số 1 được&nbsp;chạy.</p> +<p>Một lỗi không luôn xảy ra, cũng không dễ dàng nhìn&nbsp;ra.</p> +<h3>Kết&nbsp;luận</h3> +<ul> +<li>best practice không phải lúc nào cũng best, dùng biến <code>i</code> hay <code>n</code> cho vòng lặp là <span class="caps">OK</span>&nbsp;.</li> +<li>TRÁNH dùng lại tên biến. Ví dụ: + <code>py + students = ["Pikachu", "Doremon"] + students = ",".join(students)</code></li> +</ul> +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>[Deep Reading] Chọc sâu vào thư viện dùng phổ biến nhất của Python: lib requests2022-07-29T00:00:00+07:002022-07-29T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-07-29:/dh_requests.html<p><img alt="img" src="https://images.unsplash.com/photo-1470509037663-253afd7f0f51?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NTkxMDIxMDM&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Nếu 10 năm trước, khi nói bạn &#8220;học sâu&#8221;, người ta nghĩ tới việc tìm hiểu thật kỹ, chọc sâu vào qua nhiều tầng lớp để nắm thật chắc kiến thức, thì sau 10 năm marketing, Deep Learning - một nhánh của <span class="caps">AI</span>, được dịch sang tiếng Việt với tên &#8220;học …</p><p><img alt="img" src="https://images.unsplash.com/photo-1470509037663-253afd7f0f51?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NTkxMDIxMDM&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Nếu 10 năm trước, khi nói bạn &#8220;học sâu&#8221;, người ta nghĩ tới việc tìm hiểu thật kỹ, chọc sâu vào qua nhiều tầng lớp để nắm thật chắc kiến thức, thì sau 10 năm marketing, Deep Learning - một nhánh của <span class="caps">AI</span>, được dịch sang tiếng Việt với tên &#8220;học sâu&#8221;, đã chiếm mất nghĩa của từ học sâu ban&nbsp;đầu.</p> +<p>Vậy nên chúng tôi gọi là deep reading, nghe cho nó đỡ&nbsp;lẫn.</p> +<p>Loạt bài viết deep reading đi sâu vào các dòng code của các thư viện Python, xem chúng được viết thế nào, bí hiểm ra sao. Và lên thớt đầu tiên chính là thư viện được download nhiều số 1 của Python:&nbsp;requests.</p> +<h2><a href="https://github.com/psf/requests/tree/v2.28.1/requests">Cấu trúc thư&nbsp;mục</a></h2> +<p>Code thư viện nằm gọn trong 1 thư mục tên &#8220;requests&#8221;, cùng level nó có 1 thư mục tên &#8220;tests&#8221;. Trong requests, chỉ có 18 files py và không có thư mục con&nbsp;nào:</p> +<div class="highlight"><pre><span></span><code>ls requests/ +adapters.py auth.py compat.py exceptions.py hooks.py _internal_utils.py packages.py status_codes.py utils.py +api.py certs.py cookies.py help.py __init__.py models.py sessions.py structures.py __version__.py +</code></pre></div> + +<h3><a href="https://github.com/psf/requests/blob/v2.28.1/setup.py#L61-L65">Dependencies</a></h3> +<p>requests phụ thuộc trực tiếp vào 4 thư viện, đáng kể nhất là&nbsp;urllib3.</p> +<div class="highlight"><pre><span></span><code><span class="n">requires</span> <span class="o">=</span> <span class="p">[</span> + <span class="s2">&quot;charset_normalizer&gt;=2,&lt;3&quot;</span><span class="p">,</span> + <span class="s2">&quot;idna&gt;=2.5,&lt;4&quot;</span><span class="p">,</span> + <span class="s2">&quot;urllib3&gt;=1.21.1,&lt;1.27&quot;</span><span class="p">,</span> + <span class="s2">&quot;certifi&gt;=2017.4.17&quot;</span><span class="p">,</span> +<span class="p">]</span> +</code></pre></div> + +<h3><a href="https://github.com/psf/requests/blob/v2.28.1/requests/__init__.py"><code>__init__.py</code></a></h3> +<p>File đầu tiên nên đọc là <code>__init__.py</code> nó chứa những gì ta có thể truy cập khi gõ <code>requests.</code>, ví dụ như <code>requests.get</code>. File này chỉ import các function, class từ các file khác&nbsp;vào.</p> +<h3><a href="https://github.com/psf/requests/blob/v2.28.1/requests/api.py"><code>api.py</code></a></h3> +<p>Các function tiện lợi requests.get requests.post &#8230; đều nằm trong api.py. File này chứa các function ứng với các <span class="caps">HTTP</span> verb/method, và chỉ có vậy. Code cũng rất đơn&nbsp;giản:</p> +<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">request</span><span class="p">(</span><span class="n">method</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span> + <span class="k">with</span> <span class="n">sessions</span><span class="o">.</span><span class="n">Session</span><span class="p">()</span> <span class="k">as</span> <span class="n">session</span><span class="p">:</span> + <span class="k">return</span> <span class="n">session</span><span class="o">.</span><span class="n">request</span><span class="p">(</span><span class="n">method</span><span class="o">=</span><span class="n">method</span><span class="p">,</span> <span class="n">url</span><span class="o">=</span><span class="n">url</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> + + +<span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">params</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span> + <span class="sd">&quot;&quot;&quot;... đã bỏ comment cho ngắn&quot;&quot;&quot;</span> + <span class="k">return</span> <span class="n">request</span><span class="p">(</span><span class="s2">&quot;get&quot;</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">params</span><span class="o">=</span><span class="n">params</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> +</code></pre></div> + +<p><code>get</code> thực chất chỉ gọi <code>request</code>, function <code>request</code> tạo một Session và dùng nó để kết nối <span class="caps">HTTP</span> qua <code>session.request</code>.</p> +<h3><a href="https://github.com/psf/requests/blob/v2.28.1/requests/sessions.py#L355">sessions.py</a> - chứa class Session, trái tim của&nbsp;requests</h3> +<p>Một Session sẽ chứa đủ thông tin cần thiết như url, headers, auth, proxy, &#8230; rồi thực hiện gọi tới <span class="caps">HTTP</span> adapter để thực hiện kết&nbsp;nối.</p> +<h3><a href="https://github.com/psf/requests/blob/v2.28.1/requests/adapters.py#L101">adapters.py</a> - nơi kết nối thực sự xảy&nbsp;ra</h3> +<p>Dù là thực hiện kết nối <span class="caps">HTTP</span> thì trên Python cũng có rất nhiều thư viện, từ thư viện có sẵn của Python stdlib cho tới thư viện bên ngoài và dùng mặc định cho requests: urllib3. Function <a href="https://github.com/psf/requests/blob/v2.28.1/requests/adapters.py#L436"><code>send</code></a> trong adapters thực hiện kết nối với &#8220;Request&#8221; được chuẩn bị, và trả về Response. Code của send dài 150 dòng, không có gì quá phức&nbsp;tạp.</p> +<p>Còn gì không? còn nhiều và bạn có thể tự khám phá, nhưng từng ấy cũng đủ vừa sâu để hiểu rõ hơn về requests, hơn hàng ngàn lập trình viên ngoài kia&nbsp;rồi.</p> +<p>Happy deeeeeep&nbsp;reading.</p> +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Python3 str là unicode và rắc rối với utf-82022-07-25T21:35:00+07:002022-07-25T21:35:00+07:00Pymier0tag:n.pymi.vn,2022-07-25:/py3utf8.html<p>Python3 chỉ có 1 kiểu string là <code>str</code>. +Python2 có 2 kiểu string: <code>str</code> và <code>unicode</code></p> +<p>Sự hợp nhất này chính là <a href="https://docs.python.org/3/howto/unicode.html">ưu điểm rất lớn của Python3</a>, lập trình viên không phải đau đầu khi chuyển đổi giữa 2 kiểu&nbsp;string.</p> +<p>Python3 mặc định sử dụng <span class="caps">UTF</span>-8, với …</p><p>Python3 chỉ có 1 kiểu string là <code>str</code>. +Python2 có 2 kiểu string: <code>str</code> và <code>unicode</code></p> +<p>Sự hợp nhất này chính là <a href="https://docs.python.org/3/howto/unicode.html">ưu điểm rất lớn của Python3</a>, lập trình viên không phải đau đầu khi chuyển đổi giữa 2 kiểu&nbsp;string.</p> +<p>Python3 mặc định sử dụng <span class="caps">UTF</span>-8, với các giá trị <span class="caps">ASCII</span> &lt; 128 (bảng chữ cái tiếng Anh, các số, các dấu) được giữ nguyên kích thước là 1&nbsp;byte.</p> +<ul> +<li>If the code point is &lt; 128, it’s represented by the corresponding byte&nbsp;value.</li> +<li>If the code point is &gt;= 128, it’s turned into a sequence of two, three, or four bytes, where each byte of the sequence is between 128 and&nbsp;255.</li> +</ul> +<p>Cho nên khi viết Python2: print &#8220;PyMiEr2022&#8221; và Python3 print(&#8220;PyMiEr2022&#8221;) cho kết quả như&nbsp;nhau.</p> +<p>Vì một số giá trị char &lt; 128 không phải &#8220;ký tự&#8221; bình thường, có thể dùng dạng hex để viết nó, ví dụ print &#8220;\x13\x37&#8221; hay python3 print(&#8220;\x13\x37&#8221;) cho kêt quả như&nbsp;nhau.</p> +<p>Nhưng khi viết: python2 <code>print "\xaa"</code> so với Python3 <code>print("\xaa")</code> kết quả lại <strong>khác nhau</strong>:</p> +<div class="highlight"><pre><span></span><code><span class="err">$</span> <span class="n">python2</span> <span class="o">-</span><span class="n">c</span> <span class="s1">&#39;print &quot;</span><span class="se">\xaa</span><span class="s1">&quot;&#39;</span> +<span class="err">�</span> +<span class="err">$</span> <span class="n">python3</span> <span class="o">-</span><span class="n">c</span> <span class="s1">&#39;print(&quot;</span><span class="se">\xaa</span><span class="s1">&quot;)&#39;</span> +<span class="n">ª</span> +</code></pre></div> + +<p>Sử dụng lệnh <code>hexdump</code> để nhìn output ở dạng&nbsp;hex:</p> +<div class="highlight"><pre><span></span><code><span class="err">$</span> <span class="n">python2</span> <span class="o">-</span><span class="n">c</span> <span class="s1">&#39;print &quot;</span><span class="se">\xaa</span><span class="s1">&quot;&#39;</span> <span class="o">|</span> <span class="n">hexdump</span> +<span class="mi">0000000</span> <span class="mi">0</span><span class="n">aaa</span> +<span class="mi">0000002</span> + <span class="err">$</span> <span class="n">python3</span> <span class="o">-</span><span class="n">c</span> <span class="s1">&#39;print(&quot;</span><span class="se">\xaa</span><span class="s1">&quot;)&#39;</span> <span class="o">|</span> <span class="n">hexdump</span> +<span class="mi">0000000</span> <span class="n">aac2</span> <span class="mi">000</span><span class="n">a</span> +<span class="mi">0000003</span> +</code></pre></div> + +<p>Python2 tạo ra 2 bytes: <code>0a</code> (newline xuống dòng) và <code>aa</code>, +còn Python3 tạo ra tới 3 bytes, trong đó <code>aa</code> trở thành <code>aac2</code>. Tại&nbsp;sao?</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1568884149074-a93f17583719?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NTg3NTQxNzY&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Lý do bởi 0xaa có giá trị 170 &gt; 128, trên Python3 sẽ được biểu diễn bằng 2 bytes, với aa là giá trị, còn c2 là ký tự &#8220;control&#8221; để thêm vào cho đủ 2&nbsp;bytes.</p> +<p>Để in ra kết quả tương tự Python2,&nbsp;dùng:</p> +<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">sys</span> +<span class="n">sys</span><span class="o">.</span><span class="n">stdout</span><span class="o">.</span><span class="n">buffer</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="sa">b</span><span class="s2">&quot;</span><span class="se">\xaa</span><span class="s2">&quot;</span><span class="p">)</span> +</code></pre></div> + +<h2>Tham&nbsp;khảo</h2> +<ul> +<li><a href="https://discuss.python.org/t/unusal-behavior-of-python3-print-hex-values/15418/9">https://discuss.python.org/t/unusal-behavior-of-python3-print-hex-values/15418/9</a></li> +<li><a href="https://docs.python.org/3/howto/unicode.html">https://docs.python.org/3/howto/unicode.html</a></li> +<li><a href="https://stackoverflow.com/questions/908331/how-to-write-binary-data-to-stdout-in-python-3">https://stackoverflow.com/questions/908331/how-to-write-binary-data-to-stdout-in-python-3</a></li> +</ul> +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Lớp Python PyMivn khóa 41 tại Hà Nội khai giảng 28/7/20222022-07-25T00:00:00+07:002022-07-25T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-07-25:/pymi2207.html<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để trở thành lập trình viên #python chuyên&nbsp;nghiệp.</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1607188924640-b3382257fee7?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjQ1NDUyODg&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Học viên không ở Hà Nội có thể học qua livestream trên Youtube với kênh chat +Slack cùng các học viên Hà&nbsp;Nội.</p> +<p>Chi tiết tại trang chủ <a href="https://pymi.vn">Pymivn</a>. +Thắc mắc/hỏi đáp: <a href="https://invite.pymi.vn">slack</a></p>[hack] Biến đổi giữa các kiểu số thành byte2022-07-19T00:00:00+07:002022-07-19T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-07-19:/byt351.html<p><img alt="img" src="https://images.unsplash.com/photo-1577507801612-5e6e0200774f?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NTgyMzcwMDA&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Hệ thập phân (hệ số 10) là hệ số dùng phổ biến nhất ở loài người, 1,2,3,4,5,6,7,8,9&#8230;0 đều học +từ lớp 1 2&nbsp;3.</p> +<p>Máy tính biểu diễn mọi thứ ở hệ nhị phân (hệ số 2) với số 0 và …</p><p><img alt="img" src="https://images.unsplash.com/photo-1577507801612-5e6e0200774f?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NTgyMzcwMDA&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Hệ thập phân (hệ số 10) là hệ số dùng phổ biến nhất ở loài người, 1,2,3,4,5,6,7,8,9&#8230;0 đều học +từ lớp 1 2&nbsp;3.</p> +<p>Máy tính biểu diễn mọi thứ ở hệ nhị phân (hệ số 2) với số 0 và 1, gọi là&nbsp;binary.</p> +<p>Trên máy tính còn dùng các hệ khác như octal, hex và chúng đều có thể biểu diễn +chung ở 1 dạng gọi là&nbsp;byte.</p> +<h3>bit</h3> +<p>bit là đơn vị nhỏ nhất trên máy tính, bit có thể có 2 giá trị 0 và 1. Số khi +biểu diễn ở dạng bit viết thành binary:&nbsp;10010101011</p> +<p>Trên Python, có thể viết 0b rồi gõ giá trị, ví dụ: 0b101010 là dạng binary của số&nbsp;42.</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="mb">0b101010</span> <span class="o">==</span> <span class="mi">42</span> +<span class="kc">True</span> +</code></pre></div> + +<p>Có thể biến 42 thành str (string) biểu diễn dạng binary với function&nbsp;bin:</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="nb">bin</span><span class="p">(</span><span class="mi">42</span><span class="p">)</span> +<span class="s1">&#39;0b101010&#39;</span> +</code></pre></div> + +<p>Và biến ngược lại từ str thành số&nbsp;10:</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="nb">int</span><span class="p">(</span><span class="s1">&#39;101010&#39;</span><span class="p">,</span> <span class="n">base</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span> +<span class="mi">42</span> +</code></pre></div> + +<h3>byte</h3> +<p>Vì bit quá nhỏ, để viết được số 255 cần tới 8 bit: 11111111 rất dài dòng và +tốn giấy mực, nên người ta đổi 8 bits thành 1 byte. 1 byte có thể biểu diễn +bất kỳ giá trị trong khoảng 0-255 (2 mũ&nbsp;8).</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">254</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">x</span><span class="o">.</span><span class="n">bit_length</span><span class="p">()</span> +<span class="mi">8</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">x</span><span class="o">.</span><span class="n">to_bytes</span><span class="p">(</span><span class="n">length</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">byteorder</span><span class="o">=</span><span class="s1">&#39;little&#39;</span><span class="p">)</span> +<span class="sa">b</span><span class="s1">&#39;</span><span class="se">\xfe</span><span class="s1">&#39;</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">x</span><span class="o">.</span><span class="n">to_bytes</span><span class="p">(</span><span class="n">length</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">byteorder</span><span class="o">=</span><span class="s1">&#39;big&#39;</span><span class="p">)</span> +<span class="sa">b</span><span class="s1">&#39;</span><span class="se">\xfe</span><span class="s1">&#39;</span> +</code></pre></div> + +<p>bytes là kiểu dữ liệu trên python để biểu diễn các&#8230; byte. bytes được ký hiệu bằng chữ +b trước nội dung tương tự kiểu&nbsp;string.</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="sa">b</span><span class="s1">&#39;python&#39;</span> +<span class="sa">b</span><span class="s1">&#39;python&#39;</span> +<span class="o">&gt;&gt;&gt;</span> <span class="nb">type</span><span class="p">(</span><span class="sa">b</span><span class="s1">&#39;python&#39;</span><span class="p">)</span> +<span class="o">&lt;</span><span class="k">class</span> <span class="err">&#39;</span><span class="nc">bytes</span><span class="s1">&#39;&gt;</span> +<span class="o">&gt;&gt;&gt;</span> <span class="p">[</span><span class="n">i</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="sa">b</span><span class="s1">&#39;python&#39;</span><span class="p">]</span> +<span class="p">[</span><span class="mi">112</span><span class="p">,</span> <span class="mi">121</span><span class="p">,</span> <span class="mi">116</span><span class="p">,</span> <span class="mi">104</span><span class="p">,</span> <span class="mi">111</span><span class="p">,</span> <span class="mi">110</span><span class="p">]</span> +<span class="o">&gt;&gt;&gt;</span> <span class="p">[</span><span class="nb">ord</span><span class="p">(</span><span class="n">c</span><span class="p">)</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="s1">&#39;python&#39;</span><span class="p">]</span> +<span class="p">[</span><span class="mi">112</span><span class="p">,</span> <span class="mi">121</span><span class="p">,</span> <span class="mi">116</span><span class="p">,</span> <span class="mi">104</span><span class="p">,</span> <span class="mi">111</span><span class="p">,</span> <span class="mi">110</span><span class="p">]</span> +</code></pre></div> + +<p>Khi duyệt qua từng byte, chúng là các giá trị số&nbsp;int.</p> +<p>Khi in ra, các giá trị không có dạng ký tự biểu diễu như 0 1 2 hay a b c ?&lt;&gt;.,+ &#8230; +sẽ được biễu diễn bằng ký hiệu b&#8217;\x1f` với 1f là dạng hex của byte&nbsp;này.</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="nb">bytes</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">256</span><span class="p">))</span> +<span class="sa">b</span><span class="s1">&#39;</span><span class="se">\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f</span><span class="s1"> !&quot;#$%&amp;</span><span class="se">\&#39;</span><span class="s1">()*+,-./0123456789:;&lt;=&gt;?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[</span><span class="se">\\</span><span class="s1">]^_`abcdefghijklmnopqrstuvwxyz{|}~</span><span class="se">\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff</span><span class="s1">&#39;</span> +</code></pre></div> + +<h3>hex</h3> +<p>byte là đơn vị máy tính sử dụng, thì hex là giá trị +con người sử dụng, để dễ dàng biểu diễn byte. +Hex là hệ gồm 16 số 0123456789abcdef, với a = 10, b = 11,&#8230; f = 15. +Có thể thấy một sự trùng hợp không hề ngẫu&nbsp;nhiên:</p> +<ul> +<li>256 == 2 mũ&nbsp;8</li> +<li>16 == 2 mũ&nbsp;4</li> +<li>Các giá trị từ 0 đến 255 có thể biểu diễn ở dạng hex với 2 ký&nbsp;tự.</li> +</ul> +<p>Trên&nbsp;Python:</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="mh">0xfe</span> <span class="o">==</span> <span class="mi">254</span> +<span class="kc">True</span> +</code></pre></div> + +<p>Đổi từ số sang hex string, và ngược&nbsp;lại:</p> +<div class="highlight"><pre><span></span><code>&gt;&gt;&gt; hex(254) +&#39;0xfe&#39; +&gt;&gt;&gt; int(&#39;fe&#39;, base=16) +254 +</code></pre></div> + +<h3>đổi byte sang&nbsp;hex</h3> +<div class="highlight"><pre><span></span><code>&gt;&gt;&gt; x = b&#39;python&#39; +&gt;&gt;&gt; x.hex() +&#39;707974686f6e&#39; +&gt;&gt;&gt; bytes.fromhex(&#39;707974686f6e&#39;) +b&#39;python&#39; +</code></pre></div> + +<p>Giải 1 bài trong giải <span class="caps">CTF</span> <a href="https://ctftime.org/event/1670">imaginaryctf 2022</a>:</p> +<p>Đoạn ký tự này mã hóa cái&nbsp;gì?</p> +<div class="highlight"><pre><span></span><code><span class="n">s</span> <span class="o">=</span> <span class="s1">&#39;👎👍👍👎👍👎👎👍👎👍👍👎👎👎👍👍👎👍👍👍👎👍👎👎👎👍👍👎👎👍👍👎👎👍👍👍👍👎👍👍👎👍👍👎👎👍👎👍👎👍👍👎👍👍👍👎👎👍👍👎👎👎👍👍👎👎👍👍👎👎👎👎👎👍👍👎👎👍👎👎👎👍👍👎👍👎👎👍👎👍👍👎👍👍👍👎👎👍👍👎👎👍👍👍👎👍👎👍👍👍👍👍👎👍👍👎👍👎👎👍👎👍👍👍👎👎👍👍👎👍👎👍👍👍👍👍👎👍👍👎👍👍👍👎👎👎👍👍👎👎👎👎👎👍👍👍👎👍👎👎👎👍👎👍👍👍👍👍👎👍👍👎👎👍👎👍👎👍👍👎👍👍👍👎👎👍👍👎👎👎👍👍👎👍👍👍👎👎👍👎👎👍👍👍👍👎👎👍👎👍👍👍👎👎👎👎👎👍👍👍👎👍👎👎👎👍👍👎👍👎👎👍👎👎👍👍👎👎👎👎👎👍👍👎👍👍👍👎👎👍👎👍👍👍👍👍👎👎👍👍👎👎👎👍👎👍👍👎👎👎👍👎👎👎👍👍👎👎👍👎👎👍👍👎👎👍👎👍👎👎👍👍👎👎👎👎👎👍👍👎👎👍👎👎👎👎👍👍👎👍👎👎👎👎👍👍👎👎👍👍👎👍👍👍👍👍👎👍&#39;</span> +</code></pre></div> + +<p>Đoán 1 chút, dấu 👎 đại diện cho số 0, dấu 👍 đại diện cho số 1, +biến thành string 0, 1 rồi đổi thành số hệ 10, sau đó đổi sang hex, và biến thành&nbsp;bytes:</p> +<div class="highlight"><pre><span></span><code><span class="n">b</span> <span class="o">=</span> <span class="s1">&#39;&#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="s1">&#39;1&#39;</span> <span class="k">if</span> <span class="n">i</span> <span class="o">==</span> <span class="s1">&#39;👍&#39;</span> <span class="k">else</span> <span class="s1">&#39;0&#39;</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">s</span><span class="p">)</span> +<span class="nb">print</span><span class="p">(</span><span class="n">b</span><span class="p">)</span> +<span class="c1"># 0110100101100011011101000110011001111011011001010110111001100011001100000110010001101001011011100110011101011111011010010111001101011111011011100011000001110100010111110110010101101110011000110111001001111001011100000111010001101001001100000110111001011111001100010110001000110010011001010011000001100100001101000011001101111101</span> +<span class="nb">bytes</span><span class="o">.</span><span class="n">fromhex</span><span class="p">(</span><span class="nb">hex</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">b</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="c1"># b&#39;ictf{enc0ding_is_n0t_encrypti0n_1b2e0d43}&#39;</span> +</code></pre></div> + +<p>Thấy thú zị??? tham khảo thêm các writeup của Pymi tại <a href="https://github.com/pymivn/ctf">https://github.com/pymivn/ctf</a></p> +<p>Tham gia team <span class="caps">CTF</span> <span class="caps">PYMI</span> qua <a href="https://pymi-invite.fly.dev/">Slack pymi</a> #ctf&nbsp;nha</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Giới thiệu concurrent.futures trong Python 32022-05-22T00:00:00+07:002022-05-22T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-05-22:/concurrent.html<h2>Concurrency</h2> +<p>Concurrency là khái niệm chương trình thực hiện nhiều công việc cùng một +lúc (dễ thấy ở các chương trình có giao diện đồ họa: vừa hiển thị giao diện, +vừa kết nối đến trang web, hay các chương trình server phục vụ nhiều người dùng +cùng&nbsp;lúc).</p> +<p>Python …</p><h2>Concurrency</h2> +<p>Concurrency là khái niệm chương trình thực hiện nhiều công việc cùng một +lúc (dễ thấy ở các chương trình có giao diện đồ họa: vừa hiển thị giao diện, +vừa kết nối đến trang web, hay các chương trình server phục vụ nhiều người dùng +cùng&nbsp;lúc).</p> +<p>Python (cũng như nhiều ngôn ngữ lập trình khác) từ xưa đã có hai cách làm phổ +biến để viết concurrent code: dùng <a href="https://docs.python.org/3/library/threading.html">threading</a> hoặc <a href="https://docs.python.org/3/library/multiprocessing.html">multiprocessing</a>.</p> +<p>Python 3 giới thiệu thư viện &#8220;bậc cao&#8221; dễ dùng hơn có tên <a href="https://docs.python.org/3/library/concurrent.futures.html">concurrent.futures</a>.</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1511229577011-6b24bfc30871?crop=entropy&amp;cs=tinysrgb&amp;fm=jpg&amp;ixlib=rb-1.2.1&amp;q=80&amp;raw_url=true&amp;ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&amp;auto=format&amp;fit=crop&amp;w=600"></p> +<h2><span class="caps">CPU</span> bound <span class="amp">&amp;</span> <span class="caps">IO</span>&nbsp;bound</h2> +<p><span class="dquo">&#8220;</span>bound&#8221; ở đây hiểu theo nghĩa: chương trình tốn hầu hết thời gian <strong>thực hiện</strong> +tính toán (<span class="caps">CPU</span>) hay <strong>chờ</strong> (hệ điều hành) đọc ghi dữ liệu, bao gồm cả kết nối +mạng (<span class="caps">IO</span>).</p> +<p>CPython có một giới hạn về thiết kế khiến cho khi dùng threading, chỉ 1 thread +được chạy (dùng <span class="caps">CPU</span>) 1 lúc (<a href="https://docs.python.org/3/glossary.html#term-global-interpreter-lock">global interpreter lock</a>) , muốn dùng nhiều <span class="caps">CPU</span> phải chuyển qua dùng multiprocessing. +Thread nhẹ hơn process, máy tính bình thường có thể có hàng chục hay trăm ngàn +thread nhưng +không đủ (<span class="caps">RAM</span>) để tạo 10_000 process. Trong Python, khi chương trình <span class="caps">IO</span> bound, +có thể dùng thread, khi chương trình <span class="caps">CPU</span> bound thì dùng multiprocessing mới có +thể tăng&nbsp;tốc.</p> +<p>concurrent.futures cho phép chuyển đổi giữa threading hay multiprocessing một cách +đơn&nbsp;giản.</p> +<h2>Ví&nbsp;dụ</h2> +<p>Tính tổng các số từ 1 đến 30 triệu, 4&nbsp;lần.</p> +<p>Việc dùng concurrent.futures chỉ gồm 2&nbsp;bước:</p> +<ul> +<li>tạo Thread/Process Pool&nbsp;Executor</li> +<li>chạy executor.map với 2 argument: function sẽ được chạy ở thread/Process, và +list chứa argument cho mỗi lần gọi&nbsp;function.</li> +</ul> +<p>Trên máy có 8 <span class="caps">CPU</span>, kết qủa thấy dùng +ProcessPoolExecutor cho việc tính tóan <span class="caps">CPU</span> bound này nhanh gấp gần 4 lần +so với dùng&nbsp;ThreadPoolExecutor.</p> +<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">concurrent.futures</span> <span class="kn">import</span> <span class="n">ThreadPoolExecutor</span><span class="p">,</span> <span class="n">ProcessPoolExecutor</span> + +<span class="kn">import</span> <span class="nn">os</span> +<span class="kn">import</span> <span class="nn">time</span> + +<span class="k">def</span> <span class="nf">sumto</span><span class="p">(</span><span class="n">n</span><span class="p">):</span> + <span class="n">r</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="nb">range</span><span class="p">(</span><span class="mi">1</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">r</span> <span class="o">=</span> <span class="n">r</span> <span class="o">+</span> <span class="n">i</span> + <span class="k">return</span> <span class="n">r</span> + +<span class="n">start</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> +<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;ThreadPoolExecutor: max_workers=</span><span class="si">{</span><span class="n">os</span><span class="o">.</span><span class="n">cpu_count</span><span class="p">()</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span> +<span class="n">executor</span> <span class="o">=</span> <span class="n">ThreadPoolExecutor</span><span class="p">(</span><span class="n">max_workers</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">cpu_count</span><span class="p">())</span> + +<span class="k">for</span> <span class="n">r</span> <span class="ow">in</span> <span class="n">executor</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="n">sumto</span><span class="p">,</span> <span class="p">[</span><span class="mi">30_000_000</span><span class="p">,</span><span class="mi">30_000_000</span><span class="p">,</span><span class="mi">30_000_000</span><span class="p">,</span><span class="mi">30_000_000</span><span class="p">]):</span> + <span class="nb">print</span><span class="p">(</span><span class="n">r</span><span class="p">)</span> +<span class="nb">print</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span><span class="o">-</span><span class="n">start</span><span class="p">)</span> + + +<span class="n">start</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> +<span class="n">executor</span> <span class="o">=</span> <span class="n">ProcessPoolExecutor</span><span class="p">(</span><span class="n">max_workers</span><span class="o">=</span><span class="n">os</span><span class="o">.</span><span class="n">cpu_count</span><span class="p">())</span> + +<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;ProcessPoolExecutor: max_workers=</span><span class="si">{</span><span class="n">os</span><span class="o">.</span><span class="n">cpu_count</span><span class="p">()</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span> +<span class="k">for</span> <span class="n">r</span> <span class="ow">in</span> <span class="n">executor</span><span class="o">.</span><span class="n">map</span><span class="p">(</span><span class="n">sumto</span><span class="p">,</span> <span class="p">[</span><span class="mi">30_000_000</span><span class="p">,</span><span class="mi">30_000_000</span><span class="p">,</span><span class="mi">30_000_000</span><span class="p">,</span><span class="mi">30_000_000</span><span class="p">]):</span> + <span class="nb">print</span><span class="p">(</span><span class="n">r</span><span class="p">)</span> +<span class="nb">print</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span><span class="o">-</span><span class="n">start</span><span class="p">)</span> +</code></pre></div> + +<p>Kết&nbsp;quả</p> +<div class="highlight"><pre><span></span><code>$ python concurrent.py +ThreadPoolExecutor: <span class="nv">max_workers</span><span class="o">=</span><span class="m">8</span> +<span class="m">450000015000001</span> +<span class="m">450000015000001</span> +<span class="m">450000015000001</span> +<span class="m">450000015000001</span> +<span class="m">5</span>.539357423782349 +ProcessPoolExecutor: <span class="nv">max_workers</span><span class="o">=</span><span class="m">8</span> +<span class="m">450000015000001</span> +<span class="m">450000015000001</span> +<span class="m">450000015000001</span> +<span class="m">450000015000001</span> +<span class="m">1</span>.5521023273468018 +</code></pre></div> + +<p>Hết</p> +<h2>Tham&nbsp;khảo</h2> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Lớp Python PyMivn khóa 40 tại Hà Nội khai giảng 19/5/20222022-05-13T00:00:00+07:002022-05-13T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-05-13:/pymi2205.html<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để trở thành lập trình viên #python chuyên&nbsp;nghiệp.</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1607188924640-b3382257fee7?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjQ1NDUyODg&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Học viên không ở Hà Nội có thể học qua livestream trên Youtube với kênh chat +Slack cùng các học viên Hà&nbsp;Nội.</p> +<p>Chi tiết tại trang chủ <a href="https://pymi.vn">Pymivn</a>. +Thắc mắc/hỏi đáp: <a href="https://invite.pymi.vn">slack</a></p>rgba == rgb + alpha2022-04-13T20:00:00+07:002022-04-13T20:00:00+07:00Pymier0tag:n.pymi.vn,2022-04-13:/rgba.html<p><img alt="img" src="https://images.unsplash.com/photo-1639898831036-a9d330e25eee?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDk4NTkzNDQ&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p><a href="https://n.pymi.vn/3c.html">Bài trước</a> giới thiệu 3 kênh màu <span class="caps">RGB</span> thường dùng để biểu diễn +ảnh màu trên máy tính, với OpenCV dùng hơi ngược 1 chút: <span class="caps">BGR</span>.</p> +<p>Ngoài 3 kênh này, ảnh còn có thể có thêm 1 kênh thứ 4 nữa gọi là alpha. Và <span class="caps">RGB</span> +giờ sẽ gọi là …</p><p><img alt="img" src="https://images.unsplash.com/photo-1639898831036-a9d330e25eee?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDk4NTkzNDQ&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p><a href="https://n.pymi.vn/3c.html">Bài trước</a> giới thiệu 3 kênh màu <span class="caps">RGB</span> thường dùng để biểu diễn +ảnh màu trên máy tính, với OpenCV dùng hơi ngược 1 chút: <span class="caps">BGR</span>.</p> +<p>Ngoài 3 kênh này, ảnh còn có thể có thêm 1 kênh thứ 4 nữa gọi là alpha. Và <span class="caps">RGB</span> +giờ sẽ gọi là <span class="caps">RGBA</span>. +Kênh alpha cũng chứa giá trị từ 0-&gt;255, thể hiện độ &#8220;rõ&#8221; của ảnh. Gía trị 0 sẽ +trong suốt, giá trị 255 sẽ &#8220;không trong tí&nbsp;nào&#8221;.</p> +<p>Để đọc kênh thứ 4 từ file có kênh này (thường ở định dạng <span class="caps">PNG</span>), ta&nbsp;dùng:</p> +<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">cv2</span> <span class="k">as</span> <span class="nn">cv</span> +<span class="n">img</span> <span class="o">=</span> <span class="n">cv</span><span class="o">.</span><span class="n">imread</span><span class="p">(</span><span class="s2">&quot;/home/hvn/Pictures/python-logo.png&quot;</span><span class="p">,</span> <span class="n">cv</span><span class="o">.</span><span class="n">IMREAD_UNCHANGED</span><span class="p">)</span> +<span class="nb">print</span><span class="p">(</span><span class="n">img</span><span class="o">.</span><span class="n">shape</span><span class="p">)</span> +<span class="c1"># (82, 290, 4)</span> +<span class="n">cp</span> <span class="o">=</span> <span class="n">img</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span> +<span class="n">cv</span><span class="o">.</span><span class="n">imwrite</span><span class="p">(</span><span class="s2">&quot;alpha.png&quot;</span><span class="p">,</span> <span class="n">cp</span><span class="p">[:,:,</span> <span class="mi">3</span><span class="p">])</span> +</code></pre></div> + +<p>Tương tự bài trước, ta tách kênh alpha này ra riêng 1 file, sẽ thấy kết&nbsp;quả:</p> +<p><img alt="alpha" src="https://n.pymi.vn/images/alpha.png"></p> +<p>Hết.</p> +<h2>Tham&nbsp;khảo</h2> +<p>https://en.wikipedia.org/wiki/RGBA_color_model</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Tách 3 màu rgb từ 1 bức ảnh OpenCV + numpy2022-04-13T00:00:00+07:002022-04-13T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-04-13:/3c.html<p><img alt="img" src="https://images.unsplash.com/photo-1523717659-a250d867d6f1?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDk2OTI3NDQ&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Cách biểu diễn màu trên máy tính phổ biến dùng hệ màu <span class="caps">RGB</span> với 3 màu Red Green Blue (đỏ - xanh lục - xanh lam). Khi đọc file ảnh vào bằng opencv, mặc định sẽ có 1 array dạng (dài, rộng, 3), với 3 là 3 &#8220;kênh&#8221; màu. Để tách riêng …</p><p><img alt="img" src="https://images.unsplash.com/photo-1523717659-a250d867d6f1?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDk2OTI3NDQ&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Cách biểu diễn màu trên máy tính phổ biến dùng hệ màu <span class="caps">RGB</span> với 3 màu Red Green Blue (đỏ - xanh lục - xanh lam). Khi đọc file ảnh vào bằng opencv, mặc định sẽ có 1 array dạng (dài, rộng, 3), với 3 là 3 &#8220;kênh&#8221; màu. Để tách riêng 3 kênh màu, ta giữ nguyên 1 kênh, và gán 2 kênh còn lại bằng 0 (sử dụng numpy - vì 1 bức ảnh đọc vào từ opencv là 1 numpy&nbsp;ndarray).</p> +<div class="highlight"><pre><span></span><code><span class="c1"># $ python bgr.py ~/Pictures/python-logo.png</span> +<span class="kn">import</span> <span class="nn">sys</span> +<span class="kn">import</span> <span class="nn">cv2</span> <span class="k">as</span> <span class="nn">cv</span> + +<span class="n">cim</span> <span class="o">=</span> <span class="n">cv</span><span class="o">.</span><span class="n">imread</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span> +<span class="n">c1</span> <span class="o">=</span> <span class="n">cim</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span> +<span class="n">c1</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">0</span> +<span class="n">cv</span><span class="o">.</span><span class="n">imwrite</span><span class="p">(</span><span class="s2">&quot;blue.png&quot;</span><span class="p">,</span> <span class="n">c1</span><span class="p">)</span> + +<span class="n">c2</span> <span class="o">=</span> <span class="n">cim</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span> +<span class="n">c2</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">c2</span><span class="p">[:,:,</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span> +<span class="n">cv</span><span class="o">.</span><span class="n">imwrite</span><span class="p">(</span><span class="s2">&quot;green.png&quot;</span><span class="p">,</span> <span class="n">c2</span><span class="p">)</span> + +<span class="n">c3</span> <span class="o">=</span> <span class="n">cim</span><span class="o">.</span><span class="n">copy</span><span class="p">()</span> +<span class="n">c3</span><span class="p">[:,:,:</span><span class="mi">2</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span> +<span class="n">cv</span><span class="o">.</span><span class="n">imwrite</span><span class="p">(</span><span class="s2">&quot;red.png&quot;</span><span class="p">,</span> <span class="n">c3</span><span class="p">)</span> +<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Done&quot;</span><span class="p">)</span> +</code></pre></div> + +<p><img alt="python-logo" src="https://www.python.org/static/img/python-logo.png"> +<img alt="blue" src="https://n.pymi.vn/images/blue.png"> +<img alt="blue" src="https://n.pymi.vn/images/green.png"> +<img alt="blue" src="https://n.pymi.vn/images/red.png"></p> +<p>Kết quả thu được 3 bức ảnh với màu lần lượt là xanh lam, xanh lục, đỏ (<span class="caps">BGR</span>). Theo <a href="https://stackoverflow.com/questions/14556545/why-opencv-using-bgr-colour-space-instead-of-rgb">&#8220;cộng đồng mạng StackOverflow&#8221;</a>, đó là do &#8220;lịch sử&#8221; để lại, khi mà OpenCV dùng <span class="caps">BGR</span> chứ không theo thứ tự phổ biến <span class="caps">RGB</span>.</p> +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>OpenCV đọc ảnh đen trắng (grayscale)2022-04-11T00:00:00+07:002022-04-11T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-04-11:/grayscale.html<p>Ảnh màu trên máy tính thường sử dụng hệ màu red-green-blue (đỏ - xanh lục - xanh lam) viết tắt là <span class="caps">RGB</span>. Bộ màu này có khả năng biểu diễn 256 * 256 * 256 = 16777216 (16 triệu màu), mặc định OpenCV đọc ảnh vào ở dạng ảnh màu. Xem shape của ảnh sau …</p><p>Ảnh màu trên máy tính thường sử dụng hệ màu red-green-blue (đỏ - xanh lục - xanh lam) viết tắt là <span class="caps">RGB</span>. Bộ màu này có khả năng biểu diễn 256 * 256 * 256 = 16777216 (16 triệu màu), mặc định OpenCV đọc ảnh vào ở dạng ảnh màu. Xem shape của ảnh sau khi đọc thường có dạng (dài, rộng,&nbsp;3).</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1543739839-be746050f5b7?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDk2ODgzODA&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Ảnh đen trắng (gọi là grayscale), chỉ có 256 màu từ 0 đến 255 (từ đen tới bớt đen tới trắng). Khi đọc vào, sẽ chỉ thấy shape của array (dài,&nbsp;rộng).</p> +<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">cv2</span> <span class="k">as</span> <span class="nn">cv</span> + +<span class="n">cim</span> <span class="o">=</span> <span class="n">cv</span><span class="o">.</span><span class="n">imread</span><span class="p">(</span><span class="s2">&quot;/home/hvn/Pictures/anhmau.jpeg&quot;</span><span class="p">)</span> <span class="c1"># cv.IMREAD_COLOR</span> +<span class="nb">print</span><span class="p">(</span><span class="n">cim</span><span class="o">.</span><span class="n">shape</span><span class="p">)</span> +<span class="c1"># (1068, 600, 3)</span> + +<span class="n">gim</span> <span class="o">=</span> <span class="n">cv</span><span class="o">.</span><span class="n">imread</span><span class="p">(</span><span class="s2">&quot;/home/hvn/Pictures/anhmau.jpeg&quot;</span><span class="p">,</span> <span class="n">cv</span><span class="o">.</span><span class="n">IMREAD_GRAYSCALE</span><span class="p">)</span> +<span class="nb">print</span><span class="p">(</span><span class="n">gim</span><span class="o">.</span><span class="n">shape</span><span class="p">,</span> <span class="s2">&quot;To new crop size: &quot;</span><span class="p">,</span> <span class="n">gim</span><span class="o">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">//</span><span class="mi">2</span><span class="p">,</span> <span class="n">gim</span><span class="o">.</span><span class="n">shape</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="c1">#(1068, 600) To new crop size: 534 300</span> + +<span class="n">cut</span> <span class="o">=</span> <span class="n">gim</span><span class="p">[:</span><span class="n">gim</span><span class="o">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">//</span><span class="mi">2</span><span class="p">,</span> <span class="p">:</span><span class="n">gim</span><span class="o">.</span><span class="n">shape</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="n">cv</span><span class="o">.</span><span class="n">imwrite</span><span class="p">(</span><span class="s2">&quot;gray.jpg&quot;</span><span class="p">,</span> <span class="n">cut</span><span class="p">)</span> +</code></pre></div> + +<p>Kết quả thu được 1/4 góc trên trái bức ảnh ban đầu, đen&nbsp;trắng:</p> +<p><img alt="grayscale" src="https://n.pymi.vn/images/gray.jpg">.</p> +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Xin chào opencv-python2022-04-06T00:00:00+07:002022-04-06T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-04-06:/cv2.html<p><img alt="img" src="https://images.unsplash.com/photo-1459255418679-d6424da9ee33?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDkyNTUwMjY&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>OpenCV&nbsp;(https://opencv.org/)</p> +<blockquote> +<p>OpenCV (Open Source Computer Vision Library: http://opencv.org) is an open-source library that includes several hundreds of computer vision&nbsp;algorithms.</p> +</blockquote> +<p>là một thư viện xử lý hình ảnh (ảnh, video, &#8230;) viết bằng C++ nhưng có thể dùng từ nhiều ngôn ngữ khác nhau, trong Python …</p><p><img alt="img" src="https://images.unsplash.com/photo-1459255418679-d6424da9ee33?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDkyNTUwMjY&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>OpenCV&nbsp;(https://opencv.org/)</p> +<blockquote> +<p>OpenCV (Open Source Computer Vision Library: http://opencv.org) is an open-source library that includes several hundreds of computer vision&nbsp;algorithms.</p> +</blockquote> +<p>là một thư viện xử lý hình ảnh (ảnh, video, &#8230;) viết bằng C++ nhưng có thể dùng từ nhiều ngôn ngữ khác nhau, trong Python,&nbsp;cài</p> +<div class="highlight"><pre><span></span><code>pip install opencv-python +</code></pre></div> + +<p>sau đó để dùng <code>import cv2 as cv</code>.</p> +<h3>Mở file&nbsp;ảnh</h3> +<div class="highlight"><pre><span></span><code><span class="n">img</span> <span class="o">=</span> <span class="n">cv</span><span class="o">.</span><span class="n">imread</span><span class="p">(</span><span class="s2">&quot;/home/hvn/Pictures/python-logo.png&quot;</span><span class="p">)</span> +<span class="nb">print</span><span class="p">(</span><span class="nb">type</span><span class="p">(</span><span class="n">img</span><span class="p">))</span> +<span class="c1"># Output: &lt;class &#39;numpy.ndarray&#39;&gt;</span> +</code></pre></div> + +<p>Vậy một file ảnh trong OpenCV chính là 1 ndarray của&nbsp;numpy.</p> +<h3>Ghi&nbsp;file</h3> +<div class="highlight"><pre><span></span><code><span class="n">cv</span><span class="o">.</span><span class="n">imwrite</span><span class="p">(</span><span class="s2">&quot;filename.png&quot;</span><span class="p">,</span> <span class="n">img</span><span class="p">)</span> +</code></pre></div> + +<h2>Tham&nbsp;khảo</h2> +<ul> +<li>https://docs.opencv.org/4.x/d6/d00/tutorial_py_root.html</li> +</ul> +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Số float lớn nhất2022-03-25T00:00:00+07:002022-03-25T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-03-25:/float_max.html<p><img alt="img" src="https://images.unsplash.com/photo-1595160561341-267c085e1257?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDgyMDg4MTU&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Bài trước sau khi thử lấy 2**2048/2, xảy ra&nbsp;exception </p> +<blockquote> +<p>OverflowError: int too large to convert to&nbsp;float</p> +</blockquote> +<p>float không lớn tùy ý như&nbsp;int.</p> +<p>Số float lớn nhất? là <code>inf</code></p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="nb">float</span><span class="p">(</span><span class="s1">&#39;inf&#39;</span><span class="p">)</span> +<span class="n">inf</span> +</code></pre></div> + +<p>Số float lớn nhất sau <code>inf</code>? </p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">sys</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">sys</span><span class="o">.</span><span class="n">float_info</span><span class="o">.</span><span class="n">max</span> +<span class="mf">1.7976931348623157e+308 …</span></code></pre></div><p><img alt="img" src="https://images.unsplash.com/photo-1595160561341-267c085e1257?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDgyMDg4MTU&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Bài trước sau khi thử lấy 2**2048/2, xảy ra&nbsp;exception </p> +<blockquote> +<p>OverflowError: int too large to convert to&nbsp;float</p> +</blockquote> +<p>float không lớn tùy ý như&nbsp;int.</p> +<p>Số float lớn nhất? là <code>inf</code></p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="nb">float</span><span class="p">(</span><span class="s1">&#39;inf&#39;</span><span class="p">)</span> +<span class="n">inf</span> +</code></pre></div> + +<p>Số float lớn nhất sau <code>inf</code>? </p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">sys</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">sys</span><span class="o">.</span><span class="n">float_info</span><span class="o">.</span><span class="n">max</span> +<span class="mf">1.7976931348623157e+308</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">sys</span><span class="o">.</span><span class="n">float_info</span><span class="o">.</span><span class="n">max</span><span class="o">*</span><span class="mi">2</span> +<span class="n">inf</span> +</code></pre></div> + +<p><code>1.79 * 10**308</code>, một số có 309 chữ số, độ lớn gần bằng&nbsp;2**1024. </p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="nb">len</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="mi">2</span><span class="o">**</span><span class="mi">1024</span><span class="p">))</span> +<span class="mi">309</span> +<span class="o">&gt;&gt;&gt;</span> <span class="mi">2</span><span class="o">**</span><span class="mi">1024</span> +<span class="mi">179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137216</span> +<span class="o">&gt;&gt;&gt;</span> <span class="mi">2</span><span class="o">**</span><span class="mi">1024</span><span class="o">*</span><span class="mf">1.0</span> +<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span> + <span class="n">File</span> <span class="s2">&quot;&lt;stdin&gt;&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span> +<span class="ne">OverflowError</span><span class="p">:</span> <span class="nb">int</span> <span class="n">too</span> <span class="n">large</span> <span class="n">to</span> <span class="n">convert</span> <span class="n">to</span> <span class="nb">float</span> +<span class="o">&gt;&gt;&gt;</span> <span class="mi">2</span><span class="o">**</span><span class="mi">1024</span> <span class="o">&gt;</span> <span class="n">sys</span><span class="o">.</span><span class="n">float_info</span><span class="o">.</span><span class="n">max</span> +<span class="kc">True</span> +</code></pre></div> + +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Tính căn số lớn2022-03-23T00:00:00+07:002022-03-23T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-03-23:/big_float.html<p><img alt="img" src="https://images.unsplash.com/photo-1594013755115-76c4fe78b165?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDgwMzg1Nzg&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Trong Python, kiểu int có độ lớn tùy ý, như 2 mũ&nbsp;2048:</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">2</span><span class="o">**</span><span class="mi">2048</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">b</span> +<span class="mi">32317006071311007300714876688669951960444102669715484032130345427524655138867890893197201411522913463688717960921898019494119559150490921095088152386448283120630877367300996091750197750389652106796057638384067568276792218642619756161838094338476170470581645852036305042887575891541065808607552399123930385521914333389668342420684974786564569494856176035326322058077805659331026192708460314150258592864177116725943603718461857357598351152301645904403697613233287231227125684710820209725157101726931323469678542580656697935045997268352998638215525166389437335543602135433229604645318478604952148193555853611059596230656</span> +</code></pre></div> + +<p>cộng trừ vẫn thoải mái, nhưng&#8230; tính căn 2 (hoặc chia) thì&nbsp;sao? </p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="kn">from</span> <span class="nn">math</span> <span class="kn">import</span> <span class="n">sqrt</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">sqrt</span><span class="p">(</span><span class="n">b</span><span class="p">)</span> +<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span> + <span class="n">File</span> <span class="s2">&quot;&lt;stdin&gt;&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span> +<span class="ne">OverflowError</span><span class="p">:</span> <span class="nb">int …</span></code></pre></div><p><img alt="img" src="https://images.unsplash.com/photo-1594013755115-76c4fe78b165?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDgwMzg1Nzg&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Trong Python, kiểu int có độ lớn tùy ý, như 2 mũ&nbsp;2048:</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">2</span><span class="o">**</span><span class="mi">2048</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">b</span> +<span class="mi">32317006071311007300714876688669951960444102669715484032130345427524655138867890893197201411522913463688717960921898019494119559150490921095088152386448283120630877367300996091750197750389652106796057638384067568276792218642619756161838094338476170470581645852036305042887575891541065808607552399123930385521914333389668342420684974786564569494856176035326322058077805659331026192708460314150258592864177116725943603718461857357598351152301645904403697613233287231227125684710820209725157101726931323469678542580656697935045997268352998638215525166389437335543602135433229604645318478604952148193555853611059596230656</span> +</code></pre></div> + +<p>cộng trừ vẫn thoải mái, nhưng&#8230; tính căn 2 (hoặc chia) thì&nbsp;sao? </p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="kn">from</span> <span class="nn">math</span> <span class="kn">import</span> <span class="n">sqrt</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">sqrt</span><span class="p">(</span><span class="n">b</span><span class="p">)</span> +<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span> + <span class="n">File</span> <span class="s2">&quot;&lt;stdin&gt;&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span> +<span class="ne">OverflowError</span><span class="p">:</span> <span class="nb">int</span> <span class="n">too</span> <span class="n">large</span> <span class="n">to</span> <span class="n">convert</span> <span class="n">to</span> <span class="nb">float</span> +</code></pre></div> + +<p>Nội dung error cho thấy số này quá lớn để biến thành kiểu float (kết quả của phép chia, căn đều là float). Giải pháp là dùng&nbsp;math.isqrt</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="kn">from</span> <span class="nn">math</span> <span class="kn">import</span> <span class="n">isqrt</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">isqrt</span><span class="p">(</span><span class="n">b</span><span class="p">)</span> +<span class="mi">179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137216</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">isqrt</span><span class="p">(</span><span class="n">b</span><span class="p">)</span> <span class="o">==</span> <span class="mi">2</span><span class="o">**</span><span class="mi">1024</span> +<span class="kc">True</span> +</code></pre></div> + +<p>Docstring: </p> +<div class="highlight"><pre><span></span><code><span class="n">isqrt</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">Return</span> <span class="n">the</span> <span class="n">integer</span> <span class="n">part</span> <span class="n">of</span> <span class="n">the</span> <span class="n">square</span> <span class="n">root</span> <span class="n">of</span> <span class="n">the</span> <span class="nb">input</span><span class="o">.</span> +</code></pre></div> + +<p>kết quả không chắc CHÍNH XÁC là căn 2 (bởi không phải căn 2 số nào cũng là số nguyên), nên cần test lại nếu bình phương kết quả có bằng số ban đầu&nbsp;không.</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="n">isqrt</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> +<span class="mi">3</span> +<span class="o">&gt;&gt;&gt;</span> <span class="mi">3</span> <span class="o">**</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">10</span> +<span class="kc">False</span> + +<span class="o">&gt;&gt;&gt;</span> <span class="mi">2</span><span class="o">**</span><span class="mi">1000</span><span class="o">/</span><span class="mi">2</span> +<span class="mf">5.357543035931337e+300</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">isqrt</span><span class="p">(</span><span class="mi">2</span><span class="o">**</span><span class="mi">1000</span><span class="p">)</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="mi">1000</span> +<span class="kc">True</span> +</code></pre></div> + +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Các method của boolean True False2022-03-06T00:00:00+07:002022-03-06T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-03-06:/bool-methods.html<p><img alt="img" src="https://images.unsplash.com/photo-1512441933491-7b8cc442ed32?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDY1ODExODQ&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>True và False là hai giá trị kiểu bool trong Python, có thể nói thuộc một trong những thứ đơn giản nhất, vì chỉ có hai giá&nbsp;trị.</p> +<p>Sau khi học hành tử tế sẽ biết các operator and or not và chúng có tính <a href="https://pymi.vn/tutorial/boolean/">short-circuit</a>, và&#8230;&nbsp;hết.</p> +<p>Hỏi: giá …</p><p><img alt="img" src="https://images.unsplash.com/photo-1512441933491-7b8cc442ed32?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDY1ODExODQ&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>True và False là hai giá trị kiểu bool trong Python, có thể nói thuộc một trong những thứ đơn giản nhất, vì chỉ có hai giá&nbsp;trị.</p> +<p>Sau khi học hành tử tế sẽ biết các operator and or not và chúng có tính <a href="https://pymi.vn/tutorial/boolean/">short-circuit</a>, và&#8230;&nbsp;hết.</p> +<p>Hỏi: giá trị boolean có các methods&nbsp;nào?</p> +<p>Trả lời: what?!!! đó có lẽ là thứ mà bạn chưa từng&nbsp;thử.</p> +<p>bool trong Python thực chất inherit từ class base là <code>int</code>, nó có mọi methods của <code>int</code>. <span class="caps">WHAT</span>? int cũng có method? chính xác, mọi thứ trong Python đều là&nbsp;object.</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="nb">dir</span><span class="p">(</span><span class="kc">True</span><span class="p">)</span> <span class="o">==</span> <span class="nb">dir</span><span class="p">(</span><span class="mi">83</span><span class="p">)</span> +<span class="kc">True</span> +<span class="o">&gt;&gt;&gt;</span> <span class="nb">print</span><span class="p">(</span><span class="nb">dir</span><span class="p">(</span><span class="mi">83</span><span class="p">))</span> +<span class="p">[</span><span class="s1">&#39;__abs__&#39;</span><span class="p">,</span> <span class="s1">&#39;__add__&#39;</span><span class="p">,</span> <span class="s1">&#39;__and__&#39;</span><span class="p">,</span> <span class="s1">&#39;__bool__&#39;</span><span class="p">,</span> <span class="s1">&#39;__ceil__&#39;</span><span class="p">,</span> <span class="s1">&#39;__class__&#39;</span><span class="p">,</span> <span class="s1">&#39;__floor__&#39;</span><span class="p">,</span> <span class="s1">&#39;__floordiv__&#39;</span><span class="p">,</span> <span class="s1">&#39;__format__&#39;</span><span class="p">,</span> <span class="s1">&#39;__ge__&#39;</span><span class="p">,</span> <span class="s1">&#39;__getattribute__&#39;</span><span class="p">,</span> <span class="s1">&#39;_class__&#39;</span><span class="p">,</span> <span class="s1">&#39;__int__&#39;</span><span class="p">,</span> <span class="s1">&#39;__invert__&#39;</span><span class="p">,</span> <span class="s1">&#39;__le__&#39;</span><span class="p">,</span> <span class="s1">&#39;__lshift__&#39;</span><span class="p">,</span> <span class="s1">&#39;__lt__&#39;</span><span class="p">,</span> <span class="s1">&#39;__mpow__&#39;</span><span class="p">,</span> <span class="s1">&#39;__radd__&#39;</span><span class="p">,</span> <span class="s1">&#39;__rand__&#39;</span><span class="p">,</span> <span class="s1">&#39;__rdivmod__&#39;</span><span class="p">,</span> <span class="s1">&#39;__reduce__&#39;</span><span class="p">,</span> <span class="s1">&#39;__reduce_ex__ror__&#39;</span><span class="p">,</span> <span class="s1">&#39;__round__&#39;</span><span class="p">,</span> <span class="s1">&#39;__rpow__&#39;</span><span class="p">,</span> <span class="s1">&#39;__rrshift__&#39;</span><span class="p">,</span> <span class="s1">&#39;__rshift__&#39;</span><span class="p">,</span> <span class="s1">&#39;__rsub__sub__&#39;</span><span class="p">,</span> <span class="s1">&#39;__subclasshook__&#39;</span><span class="p">,</span> <span class="s1">&#39;__truediv__&#39;</span><span class="p">,</span> <span class="s1">&#39;__trunc__&#39;</span><span class="p">,</span> <span class="s1">&#39;__xor__&#39;</span><span class="p">,</span> <span class="s1">&#39;as_ing&#39;</span><span class="p">,</span> <span class="s1">&#39;numerator&#39;</span><span class="p">,</span> <span class="s1">&#39;real&#39;</span><span class="p">,</span> <span class="s1">&#39;to_bytes&#39;</span><span class="p">]</span> +<span class="o">&gt;&gt;&gt;</span> <span class="kc">True</span><span class="o">.</span><span class="vm">__class__</span><span class="o">.</span><span class="n">__base__</span> +<span class="o">&lt;</span><span class="k">class</span> <span class="err">&#39;</span><span class="nc">int</span><span class="s1">&#39;&gt;</span> +</code></pre></div> + +<p>Các &#8220;public&#8221;&nbsp;methods:</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="n">x</span><span class="o">.</span><span class="n">as_integer_ratio</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="o">&gt;&gt;&gt;</span> <span class="n">x</span><span class="o">.</span><span class="n">bit_length</span><span class="p">()</span> +<span class="mi">1</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">x</span><span class="o">.</span><span class="n">to_bytes</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s1">&#39;big&#39;</span><span class="p">)</span> +<span class="sa">b</span><span class="s1">&#39;</span><span class="se">\x01</span><span class="s1">&#39;</span> +</code></pre></div> + +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Base16 là gì? base là gì?2022-02-19T00:00:00+07:002022-02-19T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-02-19:/base16.html<p><img alt="img" src="https://images.unsplash.com/photo-1542722140-13cfa09585c7?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDUyNDM2ODk&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Bài trước giới thiệu <a href="https://n.pymi.vn/base64.html">Base64</a> vì nó sử dụng 64 ký tự A-Za-z0-9+/ để biểu diễn binary data tùy ý. +Ngoài Base64, trong tài liệu định nghĩa Base64 còn nhắc tới: Base16, Base32, and&nbsp;Base64.</p> +<h2>Base16</h2> +<p>Base16 sử dụng 16 ký tự để biểu diễn binary: 0-9a-f (hoặc viết hoa …</p><p><img alt="img" src="https://images.unsplash.com/photo-1542722140-13cfa09585c7?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDUyNDM2ODk&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Bài trước giới thiệu <a href="https://n.pymi.vn/base64.html">Base64</a> vì nó sử dụng 64 ký tự A-Za-z0-9+/ để biểu diễn binary data tùy ý. +Ngoài Base64, trong tài liệu định nghĩa Base64 còn nhắc tới: Base16, Base32, and&nbsp;Base64.</p> +<h2>Base16</h2> +<p>Base16 sử dụng 16 ký tự để biểu diễn binary: 0-9a-f (hoặc viết hoa 0-9A-F) hay còn có tên gọi phổ biến là hex. Mỗi 8bits (1byte) sẽ được chia làm 2 phần 4 bits, mỗi 4 bits có khả năng biểu diễn 2**4 == 16 giá&nbsp;trị.</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="nb">hex</span><span class="p">(</span><span class="mi">2022</span><span class="p">)</span> +<span class="s1">&#39;0x7e6&#39;</span> +<span class="o">&gt;&gt;&gt;</span> <span class="nb">bin</span><span class="p">(</span><span class="mi">2022</span><span class="p">)</span> +<span class="s1">&#39;0b11111100110&#39;</span> +</code></pre></div> + +<p>Chia bit ra thành các nhóm&nbsp;4:</p> +<p><span class="quo">&#8216;</span>111-1110-0110&#8217; +111 chưa đủ 4, thêm số 0 vào trước không thay đổi ý&nbsp;nghĩa.</p> +<ul> +<li>0111 là&nbsp;7</li> +<li>1110 là&nbsp;e</li> +<li>0110 là&nbsp;6</li> +</ul> +<p>hex của giá trị số nguyên 2022 là&nbsp;7e6.</p> +<p>Để đổi từ string biểu diễn hex về int, dùng <code>int</code></p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="nb">int</span><span class="p">(</span><span class="s1">&#39;0x7e6&#39;</span><span class="p">,</span> <span class="mi">16</span><span class="p">)</span> +<span class="mi">2022</span> +</code></pre></div> + +<p>Chú ý function hex chỉ nhận đầu vào là số int. +Trong khi Base16 có thể biểu diễn mọi giá trị, ta cần dùng thư viện&nbsp;base64:</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="n">base64</span><span class="o">.</span><span class="n">b16encode</span><span class="p">(</span><span class="sa">b</span><span class="s1">&#39;PyMi.vn&#39;</span><span class="p">)</span> +<span class="sa">b</span><span class="s1">&#39;50794D692E766E&#39;</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">base64</span><span class="o">.</span><span class="n">b16decode</span><span class="p">(</span><span class="sa">b</span><span class="s1">&#39;50794D692E766E&#39;</span><span class="p">)</span> +<span class="sa">b</span><span class="s1">&#39;PyMi.vn&#39;</span> +</code></pre></div> + +<p>Các function này nhận đầu vào là các bytes, nên không cho số vào trực tiếp&nbsp;được:</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="n">base64</span><span class="o">.</span><span class="n">b16encode</span><span class="p">(</span><span class="mi">2022</span><span class="p">)</span> +<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span> + <span class="n">File</span> <span class="s2">&quot;&lt;stdin&gt;&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span> + <span class="n">File</span> <span class="s2">&quot;/usr/lib/python3.8/base64.py&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">250</span><span class="p">,</span> <span class="ow">in</span> <span class="n">b16encode</span> + <span class="k">return</span> <span class="n">binascii</span><span class="o">.</span><span class="n">hexlify</span><span class="p">(</span><span class="n">s</span><span class="p">)</span><span class="o">.</span><span class="n">upper</span><span class="p">()</span> +<span class="ne">TypeError</span><span class="p">:</span> <span class="n">a</span> <span class="nb">bytes</span><span class="o">-</span><span class="n">like</span> <span class="nb">object</span> <span class="ow">is</span> <span class="n">required</span><span class="p">,</span> <span class="ow">not</span> <span class="s1">&#39;int&#39;</span> +</code></pre></div> + +<p>Có thể đổi từ int qua byte bằng method <code>.to_bytes()</code></p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="n">base64</span><span class="o">.</span><span class="n">b16encode</span><span class="p">((</span><span class="mi">2022</span><span class="p">)</span><span class="o">.</span><span class="n">to_bytes</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s1">&#39;big&#39;</span><span class="p">))</span> +<span class="sa">b</span><span class="s1">&#39;07E6&#39;</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">base64</span><span class="o">.</span><span class="n">b16encode</span><span class="p">((</span><span class="mi">2022</span><span class="p">)</span><span class="o">.</span><span class="n">to_bytes</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s1">&#39;little&#39;</span><span class="p">))</span> +<span class="sa">b</span><span class="s1">&#39;E607&#39;</span> +</code></pre></div> + +<div class="highlight"><pre><span></span><code><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="nv">to_bytes</span><span class="ss">(</span><span class="nv">self</span>,<span class="w"> </span><span class="o">/</span>,<span class="w"> </span><span class="nv">length</span>,<span class="w"> </span><span class="nv">byteorder</span>,<span class="w"> </span><span class="o">*</span>,<span class="w"> </span><span class="nv">signed</span><span class="o">=</span><span class="nv">False</span><span class="ss">)</span><span class="w"></span> +<span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="k">Return</span><span class="w"> </span><span class="nv">an</span><span class="w"> </span><span class="nv">array</span><span class="w"> </span><span class="nv">of</span><span class="w"> </span><span class="nv">bytes</span><span class="w"> </span><span class="nv">representing</span><span class="w"> </span><span class="nv">an</span><span class="w"> </span><span class="nv">integer</span>.<span class="w"></span> +</code></pre></div> + +<p><span class="quo">&#8216;</span>big&#8217; hay &#8216;little&#8217; là viết tắt của big-endian, và little-endian, thứ tự sắp xếp các byte trên máy&nbsp;tính.</p> +<p>Để kiểm tra máy mình dùng order nào&nbsp;gõ:</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">sys</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">sys</span><span class="o">.</span><span class="n">byteorder</span> +<span class="s1">&#39;little&#39;</span> +</code></pre></div> + +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="n">base64</span><span class="o">.</span><span class="n">b16encode</span><span class="p">((</span><span class="mi">2022</span><span class="p">)</span><span class="o">.</span><span class="n">to_bytes</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s1">&#39;big&#39;</span><span class="p">))</span> +<span class="sa">b</span><span class="s1">&#39;07E6&#39;</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">base64</span><span class="o">.</span><span class="n">b16encode</span><span class="p">((</span><span class="mi">2022</span><span class="p">)</span><span class="o">.</span><span class="n">to_bytes</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s1">&#39;little&#39;</span><span class="p">))</span> +<span class="sa">b</span><span class="s1">&#39;E607&#39;</span> +</code></pre></div> + +<p>Thứ tự giống như việc người Việt đọc truyện tranh từ trái qua phải thì người Nhật đọc từ phải qua trái, không có kiểu nào là &#8220;sai&#8221; cả. Trên máy tính cũng vậy, dùng little hay big đều không&nbsp;&#8220;sai&#8221;.</p> +<p>2022 khi viết thành dạng binary:&nbsp;&#8216;111-1110-0110&#8217;</p> +<ul> +<li>0111 là&nbsp;7</li> +<li>1110 là&nbsp;e</li> +<li>0110 là&nbsp;6</li> +</ul> +<p>Sử dụng little-endian, ta viết phần nhỏ trước, mỗi lần lấy 1 byte (== 8bits == 2 nhóm 4 bits), vậy có E6, rồi 07 =&gt;&nbsp;E607.</p> +<p><span class="caps">PS</span>: <a href="https://learnmeabitcoin.com/technical/little-endian">bitcoin cũng dùng&nbsp;little-endian</a></p> +<p>Ở đây thấy chút trái ngược, vậy 2022 là E607 hay&nbsp;07E6? </p> +<p>Cả 2 đều đúng, phụ thuộc vào việc đang sử dụng big hay little endian. Python int, hex và số (literal) dùng big&nbsp;endian.</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="mh">0x7e6</span> +<span class="mi">2022</span> +<span class="o">&gt;&gt;&gt;</span> <span class="mh">0x07e6</span> +<span class="mi">2022</span> +</code></pre></div> + +<h2>Base</h2> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="n">help</span><span class="p">(</span><span class="nb">int</span><span class="p">)</span> +<span class="nb">int</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">base</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">integer</span> +</code></pre></div> + +<p>Base là hệ cơ số, hệ nhị phân là base 2, hệ bát phân (oct) là base 8, hệ thập phân (decimal) là base 10, hex là hệ base&nbsp;16.</p> +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Thư viện base64 trong Python được viết thế nào2022-02-16T00:00:00+07:002022-02-16T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-02-16:/base64impl.html<p><img alt="img" src="https://images.unsplash.com/photo-1507499739999-097706ad8914?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDQ5Nzk4Njk&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p><a href="https://n.pymi.vn/base64.html">Bài trước</a> đã tự viết code Python để encoding từ bytes sang base64, code đơn giản, mang tính chất minh họa. Vậy stdlib base64 Python viết thế&nbsp;nào?</p> +<p><a href="https://github.com/python/cpython/blob/3.10/Lib/base64.py#L51-L62">https://github.com/python/cpython/blob/3.10/Lib/base64.py#L51-L62</a></p> +<div class="highlight"><pre><span></span><code><span class="c1"># Base64 encoding/decoding uses binascii</span> + +<span class="k">def</span> <span class="nf">b64encode</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">altchars</span><span class="o">=</span><span class="kc">None …</span></code></pre></div><p><img alt="img" src="https://images.unsplash.com/photo-1507499739999-097706ad8914?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDQ5Nzk4Njk&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p><a href="https://n.pymi.vn/base64.html">Bài trước</a> đã tự viết code Python để encoding từ bytes sang base64, code đơn giản, mang tính chất minh họa. Vậy stdlib base64 Python viết thế&nbsp;nào?</p> +<p><a href="https://github.com/python/cpython/blob/3.10/Lib/base64.py#L51-L62">https://github.com/python/cpython/blob/3.10/Lib/base64.py#L51-L62</a></p> +<div class="highlight"><pre><span></span><code><span class="c1"># Base64 encoding/decoding uses binascii</span> + +<span class="k">def</span> <span class="nf">b64encode</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">altchars</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span> + <span class="sd">&quot;&quot;&quot;Encode the bytes-like object s using Base64 and return a bytes object.</span> +<span class="sd"> Optional altchars should be a byte string of length 2 which specifies an</span> +<span class="sd"> alternative alphabet for the &#39;+&#39; and &#39;/&#39; characters. This allows an</span> +<span class="sd"> application to e.g. generate url or filesystem safe Base64 strings.</span> +<span class="sd"> &quot;&quot;&quot;</span> + <span class="n">encoded</span> <span class="o">=</span> <span class="n">binascii</span><span class="o">.</span><span class="n">b2a_base64</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">newline</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> + <span class="k">if</span> <span class="n">altchars</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span> + <span class="k">assert</span> <span class="nb">len</span><span class="p">(</span><span class="n">altchars</span><span class="p">)</span> <span class="o">==</span> <span class="mi">2</span><span class="p">,</span> <span class="nb">repr</span><span class="p">(</span><span class="n">altchars</span><span class="p">)</span> + <span class="k">return</span> <span class="n">encoded</span><span class="o">.</span><span class="n">translate</span><span class="p">(</span><span class="nb">bytes</span><span class="o">.</span><span class="n">maketrans</span><span class="p">(</span><span class="sa">b</span><span class="s1">&#39;+/&#39;</span><span class="p">,</span> <span class="n">altchars</span><span class="p">))</span> + <span class="k">return</span> <span class="n">encoded</span> +</code></pre></div> + +<p>hóa ra base64 lại gọi 1 thư viện khác <code>binascii</code> để thực hiện việc&nbsp;encoding.</p> +<blockquote> +<p>The binascii module contains a number of methods to convert between binary and various <span class="caps">ASCII</span>-encoded binary representations. Normally, you will not use these functions directly but use wrapper modules like uu, base64, or binhex&nbsp;instead. </p> +</blockquote> +<p><strong>The binascii module contains low-level functions written in C for greater speed that are used by the higher-level&nbsp;modules.</strong></p> +<p><a href="https://docs.python.org/3/library/binascii.html">https://docs.python.org/3/library/binascii.html</a></p> +<p>Chú ý function <code>b64encode(s, altchars=None)</code> nhận 1 đầu vào optional altchars, là 2 ký tự thay thế <code>+/</code> (thường là <code>-_</code> để encode <span class="caps">URL</span>).</p> +<p>vậy <a href="https://github.com/python/cpython/blob/3.8/Modules/binascii.c#L570">binascii là thư viện viết bằng C</a>, để có được tốc độ nhanh&nbsp;hơn. </p> +<p>Tò mò xem PyPy - Python viết bằng Python thì&nbsp;sao:</p> +<div class="highlight"><pre><span></span><code><span class="n">table_b2a_base64</span> <span class="o">=</span> <span class="p">(</span> + <span class="s2">&quot;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/&quot;</span><span class="p">)</span> + +<span class="nd">@unwrap_spec</span><span class="p">(</span><span class="nb">bin</span><span class="o">=</span><span class="s1">&#39;bufferstr&#39;</span><span class="p">,</span> <span class="n">newline</span><span class="o">=</span><span class="nb">bool</span><span class="p">)</span> +<span class="k">def</span> <span class="nf">b2a_base64</span><span class="p">(</span><span class="n">space</span><span class="p">,</span> <span class="nb">bin</span><span class="p">,</span> <span class="n">__kwonly__</span><span class="p">,</span> <span class="n">newline</span><span class="o">=</span><span class="kc">True</span><span class="p">):</span> + <span class="s2">&quot;Base64-code line of data.&quot;</span> + + <span class="n">newlength</span> <span class="o">=</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="nb">bin</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">3</span> + <span class="k">try</span><span class="p">:</span> + <span class="n">newlength</span> <span class="o">=</span> <span class="n">ovfcheck</span><span class="p">(</span><span class="n">newlength</span> <span class="o">*</span> <span class="mi">4</span><span class="p">)</span> + <span class="k">except</span> <span class="ne">OverflowError</span><span class="p">:</span> + <span class="k">raise</span> <span class="n">OperationError</span><span class="p">(</span><span class="n">space</span><span class="o">.</span><span class="n">w_MemoryError</span><span class="p">,</span> <span class="n">space</span><span class="o">.</span><span class="n">w_None</span><span class="p">)</span> + <span class="n">newlength</span> <span class="o">+=</span> <span class="mi">1</span> + <span class="n">res</span> <span class="o">=</span> <span class="n">StringBuilder</span><span class="p">(</span><span class="n">newlength</span><span class="p">)</span> + + <span class="n">leftchar</span> <span class="o">=</span> <span class="mi">0</span> + <span class="n">leftbits</span> <span class="o">=</span> <span class="mi">0</span> + <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="nb">bin</span><span class="p">:</span> + <span class="c1"># Shift into our buffer, and output any 6bits ready</span> + <span class="n">leftchar</span> <span class="o">=</span> <span class="p">(</span><span class="n">leftchar</span> <span class="o">&lt;&lt;</span> <span class="mi">8</span><span class="p">)</span> <span class="o">|</span> <span class="nb">ord</span><span class="p">(</span><span class="n">c</span><span class="p">)</span> + <span class="n">leftbits</span> <span class="o">+=</span> <span class="mi">8</span> + <span class="n">res</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">table_b2a_base64</span><span class="p">[(</span><span class="n">leftchar</span> <span class="o">&gt;&gt;</span> <span class="p">(</span><span class="n">leftbits</span><span class="o">-</span><span class="mi">6</span><span class="p">))</span> <span class="o">&amp;</span> <span class="mh">0x3f</span><span class="p">])</span> + <span class="n">leftbits</span> <span class="o">-=</span> <span class="mi">6</span> + <span class="k">if</span> <span class="n">leftbits</span> <span class="o">&gt;=</span> <span class="mi">6</span><span class="p">:</span> + <span class="n">res</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">table_b2a_base64</span><span class="p">[(</span><span class="n">leftchar</span> <span class="o">&gt;&gt;</span> <span class="p">(</span><span class="n">leftbits</span><span class="o">-</span><span class="mi">6</span><span class="p">))</span> <span class="o">&amp;</span> <span class="mh">0x3f</span><span class="p">])</span> + <span class="n">leftbits</span> <span class="o">-=</span> <span class="mi">6</span> + <span class="c1">#</span> + <span class="k">if</span> <span class="n">leftbits</span> <span class="o">==</span> <span class="mi">2</span><span class="p">:</span> + <span class="n">res</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">table_b2a_base64</span><span class="p">[(</span><span class="n">leftchar</span> <span class="o">&amp;</span> <span class="mi">3</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">4</span><span class="p">])</span> + <span class="n">res</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">PAD</span><span class="p">)</span> + <span class="n">res</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">PAD</span><span class="p">)</span> + <span class="k">elif</span> <span class="n">leftbits</span> <span class="o">==</span> <span class="mi">4</span><span class="p">:</span> + <span class="n">res</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">table_b2a_base64</span><span class="p">[(</span><span class="n">leftchar</span> <span class="o">&amp;</span> <span class="mh">0xf</span><span class="p">)</span> <span class="o">&lt;&lt;</span> <span class="mi">2</span><span class="p">])</span> + <span class="n">res</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">PAD</span><span class="p">)</span> + <span class="k">if</span> <span class="n">newline</span><span class="p">:</span> + <span class="n">res</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s1">&#39;</span><span class="se">\n</span><span class="s1">&#39;</span><span class="p">)</span> + <span class="k">return</span> <span class="n">space</span><span class="o">.</span><span class="n">newbytes</span><span class="p">(</span><span class="n">res</span><span class="o">.</span><span class="n">build</span><span class="p">())</span> +</code></pre></div> + +<p><a href="https://foss.heptapod.net/pypy/pypy/-/blob/branch/py3.8/pypy/module/binascii/interp_base64.py#L92-124">https://foss.heptapod.net/pypy/pypy/-/blob/branch/py3.8/pypy/module/binascii/interp_base64.py#L92-124</a></p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Base64 là gì? tại sao lại là 64?2022-02-15T00:00:00+07:002022-02-15T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-02-15:/base64.html<p><img alt="img" src="https://images.unsplash.com/photo-1580068829493-ee627a9eaf3b?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDQ5Mzg3MDY&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Khi lập trình, hay làm sysadmin, một khái niệm đâu đó với tên encode Base64 xuất hiện và không quá khó để&nbsp;dùng:</p> +<p>Lệnh trên Ubuntu&nbsp;Linux:</p> +<div class="highlight"><pre><span></span><code><span class="nb">echo</span> -n PyMi.vn <span class="p">|</span> base64 +<span class="nv">UHlNaS52bg</span><span class="o">==</span> +</code></pre></div> + +<p>Hay&nbsp;Python3:</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">base64</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">base64</span><span class="o">.</span><span class="n">b64encode</span><span class="p">(</span><span class="s2">&quot;PyMi.vn&quot;</span><span class="p">)</span> +<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span> + <span class="n">File</span> <span class="s2">&quot;&lt;stdin&gt;&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1 …</span></code></pre></div><p><img alt="img" src="https://images.unsplash.com/photo-1580068829493-ee627a9eaf3b?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDQ5Mzg3MDY&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Khi lập trình, hay làm sysadmin, một khái niệm đâu đó với tên encode Base64 xuất hiện và không quá khó để&nbsp;dùng:</p> +<p>Lệnh trên Ubuntu&nbsp;Linux:</p> +<div class="highlight"><pre><span></span><code><span class="nb">echo</span> -n PyMi.vn <span class="p">|</span> base64 +<span class="nv">UHlNaS52bg</span><span class="o">==</span> +</code></pre></div> + +<p>Hay&nbsp;Python3:</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">base64</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">base64</span><span class="o">.</span><span class="n">b64encode</span><span class="p">(</span><span class="s2">&quot;PyMi.vn&quot;</span><span class="p">)</span> +<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span> + <span class="n">File</span> <span class="s2">&quot;&lt;stdin&gt;&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span> + <span class="n">File</span> <span class="s2">&quot;/usr/lib/python3.8/base64.py&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">58</span><span class="p">,</span> <span class="ow">in</span> <span class="n">b64encode</span> + <span class="n">encoded</span> <span class="o">=</span> <span class="n">binascii</span><span class="o">.</span><span class="n">b2a_base64</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">newline</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span> +<span class="ne">TypeError</span><span class="p">:</span> <span class="n">a</span> <span class="nb">bytes</span><span class="o">-</span><span class="n">like</span> <span class="nb">object</span> <span class="ow">is</span> <span class="n">required</span><span class="p">,</span> <span class="ow">not</span> <span class="s1">&#39;str&#39;</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">base64</span><span class="o">.</span><span class="n">b64encode</span><span class="p">(</span><span class="sa">b</span><span class="s2">&quot;PyMi.vn&quot;</span><span class="p">)</span> +<span class="sa">b</span><span class="s1">&#39;UHlNaS52bg==&#39;</span> +</code></pre></div> + +<p>một cách nhận diện dữ liệu ở dạng Base64 là gồm ký tự từ A-Z a-z 0-9 và thường kết thúc với một vài dấu <code>=</code>.</p> +<p>Nôm na thì đơn giản, chi tiết hơn sẽ có nhiều điều thú vị hay ho rất chi này&nbsp;nọ.</p> +<p>Base64 cho phép biến dữ liệu binary thành dạng text (<span class="caps">ASCII</span>). Ví dụ như 1 file ảnh, logo Python, khi viết <span class="caps">HTML</span> có thể dùng thẻ img và đường&nbsp;link:</p> +<div class="highlight"><pre><span></span><code><span class="p">&lt;</span><span class="nt">img</span> <span class="na">src</span><span class="o">=</span><span class="s">&quot;https://www.python.org/static/img/python-logo.png&quot;</span><span class="p">&gt;</span> +</code></pre></div> + +<p>Nhưng cũng có thể biến file ảnh này dạng text và&nbsp;viết:</p> +<div class="highlight"><pre><span></span><code><span class="p">&lt;</span><span class="nt">img</span> <span class="na">src</span><span class="o">=</span><span class="s">&quot;data:image/png;base64,BASE64OFIMAGE&quot;</span><span class="p">&gt;</span> +</code></pre></div> + +<p>viết kiểu này có ưu điểm không bị phụ thuộc vào việc link ảnh sống hay chết, vì toàn bộ bức ảnh đã nằm ngay trong file <span class="caps">HTML</span>. Chuột phải và copy link của bức ảnh sau để&nbsp;thấy:</p> +<p><img src="data:image/png;base64,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 +wgAAAABJRU5ErkJggg=="></p> +<p>Với Base64 thu được khi tải file logo và&nbsp;chạy</p> +<div class="highlight"><pre><span></span><code>base64 python-logo.png +</code></pre></div> + +<p>Base64 được dùng phổ biến khi dùng email với từ khóa <span class="caps">MIME</span>, để nhúng ảnh, gửi kèm file với&nbsp;email.</p> +<h2>Base64 là&nbsp;gì</h2> +<p>Theo tài liệu định nghĩa Base64 <a href="https://datatracker.ietf.org/doc/html/rfc3548.html#page-4"><span class="caps">RFC3548</span></a>:</p> +<p><strong>The Base 64 encoding is designed to represent arbitrary sequences of +octets in a form that requires case sensitivity but need not be +humanly&nbsp;readable.</strong></p> +<h2>Tại sao lại là&nbsp;64?</h2> +<p>Vì việc encoding sử dụng 64 ký tự (+1 ký tự = để&nbsp;padding).</p> +<p>64 ký tự&nbsp;gồm:</p> +<ul> +<li>26 ký tự&nbsp;A-Z</li> +<li>26 ký tự&nbsp;a-z</li> +<li>10 ký tự&nbsp;0-9</li> +<li><code>+</code> và <code>/</code> (hoặc thay bằng <code>-</code> và <code>_</code> khi encode <span class="caps">URL</span>)</li> +</ul> +<h2>Cách&nbsp;encode</h2> +<p>Biểu diễn nhóm 24-bit đầu vào thành 4 ký tự đầu&nbsp;ra.</p> +<p>24 bit tạo bởi 3 byte (3 * 8 == 24) đặt cạnh nhau từ trái qua phải, sau đó chia làm 4 phần 6 bits, mỗi phần đổi bit ra số rồi tra trong bảng xem ứng với ký tự nào. Do mỗi phần có 6 bit nên có khả năng biểu diễn 2**6 = 64 ký&nbsp;tự.</p> +<div class="highlight"><pre><span></span><code><span class="nb">+--</span><span class="c">first octet</span><span class="nb">--+-</span><span class="c">second octet</span><span class="nb">--+--</span><span class="c">third octet</span><span class="nb">--+</span><span class="c"></span> +<span class="c">|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|</span> +<span class="nb">+-----------+---+-------+-------+---+-----------+</span><span class="c"></span> +<span class="c">|5 4 3 2 1 0|5 4 3 2 1 0|5 4 3 2 1 0|5 4 3 2 1 0|</span> +<span class="nb">+--</span><span class="c">1</span><span class="nt">.</span><span class="c">index</span><span class="nb">--+--</span><span class="c">2</span><span class="nt">.</span><span class="c">index</span><span class="nb">--+--</span><span class="c">3</span><span class="nt">.</span><span class="c">index</span><span class="nb">--+--</span><span class="c">4</span><span class="nt">.</span><span class="c">index</span><span class="nb">--+</span><span class="c"></span> +</code></pre></div> + +<p>Đầu vào luôn cần là bội của 6 bits, nên nếu thiếu ta cần thêm vào đó các bit 0 +cho đủ. Ví dụ có 4 ký tự đầu vào = 8 * 4 == 32 bits - (6 bits * 5) = 2 bits. 2 bits nên +phải thêm 4 bit 0 vào sau cho đủ 6&nbsp;bit.</p> +<p>Mỗi 24 bits đầu vào sinh ra 4 ký tự Base64, nên đầu ra nếu thiếu ký tự cần thêm các ký tự <code>=</code> cho đủ bội của 4 phục vụ việc decode - chuyển ngược lại từ Base64 thành&nbsp;binary.</p> +<h2>Code Python để encode&nbsp;Base64</h2> +<p>Python có sẵn thư viện <code>base64</code> với function <code>b64encode</code> để biến bytes đầu vào thành dạng text&nbsp;Base64.</p> +<p>Code sau code theo mô tả cách thực hiện Base64 ở&nbsp;trên:</p> +<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">string</span> +<span class="n">b64</span> <span class="o">=</span> <span class="n">string</span><span class="o">.</span><span class="n">ascii_uppercase</span> <span class="o">+</span> <span class="n">string</span><span class="o">.</span><span class="n">ascii_lowercase</span> <span class="o">+</span> <span class="n">string</span><span class="o">.</span><span class="n">digits</span> <span class="o">+</span> <span class="s1">&#39;+/&#39;</span> + +<span class="n">data</span> <span class="o">=</span> <span class="s1">&#39;PyMi.vn&#39;</span><span class="o">.</span><span class="n">encode</span><span class="p">(</span><span class="s1">&#39;utf-8&#39;</span><span class="p">)</span> +<span class="n">bits</span> <span class="o">=</span> <span class="p">[]</span> +<span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">data</span><span class="p">:</span> + <span class="nb">print</span><span class="p">(</span><span class="nb">type</span><span class="p">(</span><span class="n">c</span><span class="p">),</span> <span class="n">c</span><span class="p">,</span> <span class="nb">bin</span><span class="p">(</span><span class="n">c</span><span class="p">))</span> + <span class="n">bits</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="nb">bin</span><span class="p">(</span><span class="n">c</span><span class="p">)[</span><span class="mi">2</span><span class="p">:]</span><span class="o">.</span><span class="n">zfill</span><span class="p">(</span><span class="mi">8</span><span class="p">))</span> + +<span class="n">output</span> <span class="o">=</span> <span class="p">[]</span> +<span class="k">for</span> <span class="n">i24</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">bits</span><span class="p">),</span> <span class="mi">24</span><span class="p">):</span> + <span class="n">g1</span> <span class="o">=</span> <span class="n">bits</span><span class="p">[</span><span class="n">i24</span><span class="p">:</span><span class="n">i24</span><span class="o">+</span><span class="mi">24</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">g1</span><span class="p">),</span> <span class="mi">6</span><span class="p">):</span> + <span class="n">six_bits</span> <span class="o">=</span> <span class="n">g1</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="mi">6</span><span class="p">]</span> + <span class="k">while</span> <span class="nb">len</span><span class="p">(</span><span class="n">six_bits</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">6</span><span class="p">:</span> + <span class="n">six_bits</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s1">&#39;0&#39;</span><span class="p">)</span> + <span class="n">n</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="s1">&#39;&#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">six_bits</span><span class="p">),</span> <span class="mi">2</span><span class="p">)</span> + <span class="n">output</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">b64</span><span class="p">[</span><span class="n">n</span><span class="p">])</span> + <span class="nb">print</span><span class="p">(</span><span class="n">six_bits</span><span class="p">,</span> <span class="n">n</span><span class="p">,</span> <span class="n">b64</span><span class="p">[</span><span class="n">n</span><span class="p">])</span> + +<span class="k">while</span> <span class="nb">len</span><span class="p">(</span><span class="n">output</span><span class="p">)</span> <span class="o">%</span> <span class="mi">4</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">:</span> + <span class="n">output</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s1">&#39;=&#39;</span><span class="p">)</span> + +<span class="nb">print</span><span class="p">(</span><span class="s1">&#39;&#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">output</span><span class="p">))</span> +<span class="nb">print</span><span class="p">(</span><span class="n">base64</span><span class="o">.</span><span class="n">b64encode</span><span class="p">(</span><span class="sa">b</span><span class="s1">&#39;PyMi.vn&#39;</span><span class="p">))</span> +</code></pre></div> + +<p>Output</p> +<div class="highlight"><pre><span></span><code>&lt;class &#39;int&#39;&gt; 80 0b1010000 +&lt;class &#39;int&#39;&gt; 121 0b1111001 +&lt;class &#39;int&#39;&gt; 77 0b1001101 +&lt;class &#39;int&#39;&gt; 105 0b1101001 +&lt;class &#39;int&#39;&gt; 46 0b101110 +&lt;class &#39;int&#39;&gt; 118 0b1110110 +&lt;class &#39;int&#39;&gt; 110 0b1101110 +[&#39;0&#39;, &#39;1&#39;, &#39;0&#39;, &#39;1&#39;, &#39;0&#39;, &#39;0&#39;] 20 U +[&#39;0&#39;, &#39;0&#39;, &#39;0&#39;, &#39;1&#39;, &#39;1&#39;, &#39;1&#39;] 7 H +[&#39;1&#39;, &#39;0&#39;, &#39;0&#39;, &#39;1&#39;, &#39;0&#39;, &#39;1&#39;] 37 l +[&#39;0&#39;, &#39;0&#39;, &#39;1&#39;, &#39;1&#39;, &#39;0&#39;, &#39;1&#39;] 13 N +[&#39;0&#39;, &#39;1&#39;, &#39;1&#39;, &#39;0&#39;, &#39;1&#39;, &#39;0&#39;] 26 a +[&#39;0&#39;, &#39;1&#39;, &#39;0&#39;, &#39;0&#39;, &#39;1&#39;, &#39;0&#39;] 18 S +[&#39;1&#39;, &#39;1&#39;, &#39;1&#39;, &#39;0&#39;, &#39;0&#39;, &#39;1&#39;] 57 5 +[&#39;1&#39;, &#39;1&#39;, &#39;0&#39;, &#39;1&#39;, &#39;1&#39;, &#39;0&#39;] 54 2 +[&#39;0&#39;, &#39;1&#39;, &#39;1&#39;, &#39;0&#39;, &#39;1&#39;, &#39;1&#39;] 27 b +[&#39;1&#39;, &#39;0&#39;, &#39;0&#39;, &#39;0&#39;, &#39;0&#39;, &#39;0&#39;] 32 g +UHlNaS52bg== +b&#39;UHlNaS52bg==&#39; +</code></pre></div> + +<h2>Tham&nbsp;khảo</h2> +<p>https://datatracker.ietf.org/doc/html/rfc3548.html</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Duyệt qua mọi đáp án bài toán 8 hậu trên Pygame với Z32022-01-24T00:00:00+07:002022-01-24T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-01-24:/z3pygame8q.html<p><img alt="img" src="https://images.unsplash.com/photo-1602426005943-50054a7be960?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDI5MjIwMzc&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Bài <a href="https://n.pymi.vn/z3pygame.html">trước</a> đã hiển thị 1 đáp án bài toán 8 hậu giải bằng Z3 lên Pygame. Bài này sẽ hiển thị <a href="https://n.pymi.vn/z3ineq.html">mọi đáp án</a>, mỗi lần người dùng nhấn phím <span class="caps">SPACE</span> sẽ chuyển sang đáp án tiếp&nbsp;theo.</p> +<p>Trong Pygame để nhận việc người dùng nhấn phím, dùng&nbsp;pygame …</p><p><img alt="img" src="https://images.unsplash.com/photo-1602426005943-50054a7be960?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDI5MjIwMzc&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Bài <a href="https://n.pymi.vn/z3pygame.html">trước</a> đã hiển thị 1 đáp án bài toán 8 hậu giải bằng Z3 lên Pygame. Bài này sẽ hiển thị <a href="https://n.pymi.vn/z3ineq.html">mọi đáp án</a>, mỗi lần người dùng nhấn phím <span class="caps">SPACE</span> sẽ chuyển sang đáp án tiếp&nbsp;theo.</p> +<p>Trong Pygame để nhận việc người dùng nhấn phím, dùng&nbsp;pygame.event.get:</p> +<div class="highlight"><pre><span></span><code> <span class="k">for</span> <span class="n">e</span> <span class="ow">in</span> <span class="n">pygame</span><span class="o">.</span><span class="n">event</span><span class="o">.</span><span class="n">get</span><span class="p">():</span> + <span class="k">if</span> <span class="n">e</span><span class="o">.</span><span class="n">type</span> <span class="o">==</span> <span class="n">pygame</span><span class="o">.</span><span class="n">KEYDOWN</span><span class="p">:</span> + <span class="k">if</span> <span class="n">e</span><span class="o">.</span><span class="n">key</span> <span class="o">==</span> <span class="n">pygame</span><span class="o">.</span><span class="n">K_SPACE</span><span class="p">:</span> + <span class="n">TÍNH</span> <span class="n">ĐÁP</span> <span class="n">ÁN</span> <span class="n">TIẾP</span> <span class="n">THEO</span><span class="p">,</span> <span class="n">VẼ</span> <span class="n">LẠI</span> +</code></pre></div> + +<p>Để chuyển qua đáp án tiếp theo, có 2 cách&nbsp;làm:</p> +<ul> +<li>tìm mọi đáp án, chứa sẵn trong 1 list, khi người dùng ấn <span class="caps">SPACE</span> thì chuyển tới đáp án tiếp theo. Ưu điểm: đơn giản, không cần biết khái niệm gì &#8220;lạ&#8221;, nhược điểm: tốn <span class="caps">RAM</span> để chứa 1 list các kết&nbsp;quả.</li> +<li>dùng generator, mỗi lần người dùng nhấn <span class="caps">SPACE</span> sẽ lấy ra đáp án tiếp theo. Ưu điểm: chỉ tốn <span class="caps">RAM</span> chứa 1 đáp án duy nhất, nhược điểm: generator nghe có vẻ đáng sợ mặc dù thay đổi code chỉ 1 2&nbsp;dòng.</li> +</ul> +<p>Trong function tìm nghiệm của bài toán, thay vì tạo list, append các đáp án (model), viết <code>yield model</code>. +Để lấy ra lần lượt từng đáp án, gõ&nbsp;next:</p> +<div class="highlight"><pre><span></span><code> <span class="n">solver</span> <span class="o">=</span> <span class="n">Solver</span><span class="p">()</span> + <span class="n">solver</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">columns</span> <span class="o">+</span> <span class="n">distinct</span> <span class="o">+</span> <span class="n">diags</span><span class="p">)</span> + <span class="k">while</span> <span class="n">solver</span><span class="o">.</span><span class="n">check</span><span class="p">()</span> <span class="o">==</span> <span class="n">sat</span><span class="p">:</span> + <span class="n">m</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">model</span><span class="p">()</span> + <span class="k">yield</span> <span class="n">m</span> <span class="c1"># &lt;=======</span> + + <span class="n">block</span> <span class="o">=</span> <span class="p">[]</span> + <span class="k">for</span> <span class="n">var</span> <span class="ow">in</span> <span class="n">m</span><span class="p">:</span> + <span class="n">v</span> <span class="o">=</span> <span class="n">var</span><span class="p">()</span> + <span class="n">block</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">v</span> <span class="o">!=</span> <span class="n">m</span><span class="p">[</span><span class="n">var</span><span class="p">])</span> + + <span class="n">solver</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">Or</span><span class="p">(</span><span class="n">block</span><span class="p">))</span> +</code></pre></div> + +<div class="highlight"><pre><span></span><code><span class="n">it</span> <span class="o">=</span> <span class="n">find_queens</span><span class="p">()</span> +<span class="c1"># ...</span> + +<span class="k">while</span> <span class="n">running</span><span class="p">:</span> + <span class="c1"># ...</span> + <span class="k">for</span> <span class="n">e</span> <span class="ow">in</span> <span class="n">pygame</span><span class="o">.</span><span class="n">event</span><span class="o">.</span><span class="n">get</span><span class="p">():</span> + <span class="k">if</span> <span class="n">e</span><span class="o">.</span><span class="n">type</span> <span class="o">==</span> <span class="n">pygame</span><span class="o">.</span><span class="n">KEYDOWN</span><span class="p">:</span> + <span class="k">if</span> <span class="n">e</span><span class="o">.</span><span class="n">key</span> <span class="o">==</span> <span class="n">pygame</span><span class="o">.</span><span class="n">K_SPACE</span><span class="p">:</span> + <span class="n">draw_board</span><span class="p">(</span><span class="n">board</span><span class="p">)</span> + <span class="k">try</span><span class="p">:</span> + <span class="n">m</span> <span class="o">=</span> <span class="nb">next</span><span class="p">(</span><span class="n">it</span><span class="p">)</span> <span class="c1"># &lt;=====</span> + <span class="n">counter</span> <span class="o">+=</span> <span class="mi">1</span> + <span class="k">except</span> <span class="ne">Exception</span><span class="p">:</span> + <span class="n">pygame</span><span class="o">.</span><span class="n">exit</span><span class="p">()</span> + + <span class="n">d</span> <span class="o">=</span> <span class="p">{</span><span class="n">i</span><span class="o">.</span><span class="n">name</span><span class="p">():</span> <span class="n">m</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="n">as_long</span><span class="p">()</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">m</span><span class="p">}</span> +</code></pre></div> + +<p>Thêm 1 biến đếm hiển thị lên màn hình đáp án thứ bao&nbsp;nhiêu.</p> +<div class="highlight"><pre><span></span><code> <span class="n">myfont</span> <span class="o">=</span> <span class="n">pygame</span><span class="o">.</span><span class="n">font</span><span class="o">.</span><span class="n">SysFont</span><span class="p">(</span><span class="s2">&quot;monospace&quot;</span><span class="p">,</span> <span class="mi">80</span><span class="p">)</span> + <span class="n">scoretext</span> <span class="o">=</span> <span class="n">myfont</span><span class="o">.</span><span class="n">render</span><span class="p">(</span><span class="s2">&quot;#</span><span class="si">{0}</span><span class="s2">&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">counter</span><span class="p">),</span> <span class="mi">10</span><span class="p">,</span> <span class="p">(</span><span class="mh">0xFF</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="n">display</span><span class="o">.</span><span class="n">blit</span><span class="p">(</span><span class="n">scoretext</span><span class="p">,</span> <span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">10</span><span class="p">))</span> +</code></pre></div> + +<p>Kết&nbsp;quả:</p> +<p><img alt="8q game" src="https://n.pymi.vn/images/z3pygame8qv2.png"></p> +<p>Full <a href="https://n.pymi.vn/z38qgamev2.py">code</a></p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Giải bài toán 8 hậu trên Pygame với Z32022-01-23T00:00:00+07:002022-01-23T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-01-23:/z3pygame.html<p><img alt="img" src="https://images.unsplash.com/photo-1596434934651-0cc599e2c7c1?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDI5MTEwNzg&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Pygame vốn để làm game, nhưng cũng là một công cụ tiện lợi để hiển thị các vấn đề. +Tải pygame <code>pip install pygame</code> rồi viết&nbsp;code:</p> +<p>Hiện 1 cửa sổ vuông tương ứng với bàn cờ, kích thước 800x800&nbsp;pixel:</p> +<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">time</span> +<span class="kn">import</span> <span class="nn">pygame</span> +<span class="n">pygame</span><span class="o">.</span><span class="n">init</span><span class="p">()</span> + +<span class="n">display</span> <span class="o">=</span> <span class="n">pygame</span><span class="o">.</span><span class="n">display …</span></code></pre></div><p><img alt="img" src="https://images.unsplash.com/photo-1596434934651-0cc599e2c7c1?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDI5MTEwNzg&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Pygame vốn để làm game, nhưng cũng là một công cụ tiện lợi để hiển thị các vấn đề. +Tải pygame <code>pip install pygame</code> rồi viết&nbsp;code:</p> +<p>Hiện 1 cửa sổ vuông tương ứng với bàn cờ, kích thước 800x800&nbsp;pixel:</p> +<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">time</span> +<span class="kn">import</span> <span class="nn">pygame</span> +<span class="n">pygame</span><span class="o">.</span><span class="n">init</span><span class="p">()</span> + +<span class="n">display</span> <span class="o">=</span> <span class="n">pygame</span><span class="o">.</span><span class="n">display</span><span class="o">.</span><span class="n">set_mode</span><span class="p">((</span><span class="mi">800</span><span class="p">,</span> <span class="mi">800</span><span class="p">))</span> +<span class="n">pygame</span><span class="o">.</span><span class="n">display</span><span class="o">.</span><span class="n">set_caption</span><span class="p">(</span><span class="s2">&quot;Z3 8 queens - pymi.vn&quot;</span><span class="p">)</span> +<span class="n">running</span> <span class="o">=</span> <span class="kc">True</span> +<span class="k">while</span> <span class="n">running</span><span class="p">:</span> + <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> +</code></pre></div> + +<p>Tạo bề mặt để vẽ, là 1 object Surface, đổ nền trắng, và tính toán để tô đen các ô đan xen. +Hệ màu được sử dụng là <span class="caps">RGB</span> (Red, Green, Blue), mỗi màu được thể hiện bằng 3 con số trong khoảng 0-255, viết bằng hệ 16 (hexadecimal) sẽ có dạng 0x00 -&gt;&nbsp;0xff.</p> +<div class="highlight"><pre><span></span><code><span class="n">white</span> <span class="o">=</span> <span class="p">(</span><span class="mh">0xff</span><span class="p">,</span> <span class="mh">0xff</span><span class="p">,</span> <span class="mh">0xff</span><span class="p">)</span> +<span class="n">black</span> <span class="o">=</span> <span class="p">(</span><span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">,</span> <span class="mh">0x00</span><span class="p">)</span> + +<span class="n">cell_size</span> <span class="o">=</span> <span class="mi">100</span> +<span class="n">board</span> <span class="o">=</span> <span class="n">pygame</span><span class="o">.</span><span class="n">Surface</span><span class="p">((</span><span class="n">cell_size</span> <span class="o">*</span> <span class="mi">8</span><span class="p">,</span> <span class="n">cell_size</span> <span class="o">*</span> <span class="mi">8</span><span class="p">))</span> +<span class="n">board</span><span class="o">.</span><span class="n">fill</span><span class="p">(</span><span class="n">white</span><span class="p">)</span> + +<span class="n">cntr</span> <span class="o">=</span> <span class="mi">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="mi">8</span><span class="p">):</span> + <span class="k">for</span> <span class="n">y</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="k">if</span> <span class="n">cntr</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="n">pygame</span><span class="o">.</span><span class="n">draw</span><span class="o">.</span><span class="n">rect</span><span class="p">(</span><span class="n">board</span><span class="p">,</span> <span class="n">black</span><span class="p">,</span> +<span class="p">(</span><span class="n">x</span><span class="o">*</span><span class="n">cell_size</span><span class="p">,</span> <span class="n">y</span><span class="o">*</span><span class="n">cell_size</span><span class="p">,</span> <span class="n">cell_size</span><span class="p">,</span> <span class="n">cell_size</span><span class="p">))</span> + <span class="n">cntr</span> <span class="o">+=</span> <span class="mi">1</span> + <span class="n">cntr</span> <span class="o">+=</span> <span class="mi">1</span> + +<span class="k">while</span> <span class="n">running</span><span class="p">:</span> + <span class="n">display</span><span class="o">.</span><span class="n">blit</span><span class="p">(</span><span class="n">board</span><span class="p">,</span> <span class="n">board</span><span class="o">.</span><span class="n">get_rect</span><span class="p">())</span> + <span class="n">pygame</span><span class="o">.</span><span class="n">display</span><span class="o">.</span><span class="n">flip</span><span class="p">()</span> + <span class="n">time</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> +</code></pre></div> + +<p>Đã được bàn cờ với các ô đen và&nbsp;trắng.</p> +<p>Mang code bài <a href="https://n.pymi.vn/z38q.html">giải 8 hậu trước</a> ghép vào, dùng 1 bức ảnh bên ngoài để đại diện cho quân&nbsp;hậu:</p> +<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">find_queens</span><span class="p">():</span> + <span class="kn">from</span> <span class="nn">z3</span> <span class="kn">import</span> <span class="n">Int</span><span class="p">,</span> <span class="n">And</span><span class="p">,</span> <span class="n">Distinct</span><span class="p">,</span> <span class="n">Solver</span> + <span class="n">queens</span> <span class="o">=</span> <span class="p">[</span><span class="n">Int</span><span class="p">(</span><span class="sa">f</span><span class="s1">&#39;Q</span><span class="si">{</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="si">}</span><span class="s1">&#39;</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">8</span><span class="p">)]</span> + <span class="n">columns</span> <span class="o">=</span> <span class="p">[</span><span class="n">And</span><span class="p">(</span><span class="mi">1</span> <span class="o">&lt;=</span> <span class="n">q</span><span class="p">,</span> <span class="n">q</span> <span class="o">&lt;=</span><span class="mi">8</span><span class="p">)</span> <span class="k">for</span> <span class="n">q</span> <span class="ow">in</span> <span class="n">queens</span><span class="p">]</span> + <span class="n">distinct</span> <span class="o">=</span> <span class="p">[</span><span class="n">Distinct</span><span class="p">(</span><span class="n">queens</span><span class="p">)]</span> + <span class="n">diags</span> <span class="o">=</span> <span class="p">[</span><span class="n">And</span><span class="p">(</span><span class="n">queens</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">-</span> <span class="n">queens</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="o">-</span> <span class="n">j</span><span class="p">,</span> + <span class="n">queens</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">-</span> <span class="n">queens</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">!=</span> <span class="n">j</span> <span class="o">-</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="nb">range</span><span class="p">(</span><span class="mi">8</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="n">i</span><span class="p">)]</span> + + <span class="n">solver</span> <span class="o">=</span> <span class="n">Solver</span><span class="p">()</span> + <span class="n">solver</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">columns</span><span class="o">+</span> <span class="n">distinct</span> <span class="o">+</span> <span class="n">diags</span><span class="p">)</span> + <span class="n">solver</span><span class="o">.</span><span class="n">check</span><span class="p">()</span> + <span class="n">m</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">model</span><span class="p">()</span> + <span class="k">return</span> <span class="n">m</span> + +<span class="c1">#...</span> +<span class="n">m</span> <span class="o">=</span> <span class="n">find_queens</span><span class="p">()</span> +<span class="n">d</span> <span class="o">=</span> <span class="p">{</span><span class="n">i</span><span class="o">.</span><span class="n">name</span><span class="p">():</span> <span class="n">m</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="n">as_long</span><span class="p">()</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">m</span><span class="p">}</span> +<span class="n">image</span> <span class="o">=</span> <span class="n">pygame</span><span class="o">.</span><span class="n">image</span><span class="o">.</span><span class="n">load</span><span class="p">(</span><span class="s2">&quot;beerqueen.PNG&quot;</span><span class="p">)</span> + +<span class="k">while</span> <span class="n">running</span><span class="p">:</span> + <span class="n">display</span><span class="o">.</span><span class="n">blit</span><span class="p">(</span><span class="n">board</span><span class="p">,</span> <span class="n">board</span><span class="o">.</span><span class="n">get_rect</span><span class="p">())</span> + + <span class="k">for</span> <span class="n">row</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="k">for</span> <span class="n">col</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="k">if</span> <span class="n">row</span><span class="o">+</span><span class="mi">1</span> <span class="o">==</span> <span class="n">d</span><span class="p">[</span><span class="sa">f</span><span class="s1">&#39;Q</span><span class="si">{</span><span class="n">col</span><span class="o">+</span><span class="mi">1</span><span class="si">}</span><span class="s1">&#39;</span><span class="p">]:</span> + <span class="n">board</span><span class="o">.</span><span class="n">blit</span><span class="p">(</span><span class="n">image</span><span class="p">,</span> <span class="p">(</span><span class="n">row</span><span class="o">*</span><span class="n">cell_size</span><span class="p">,</span> <span class="n">col</span> <span class="o">*</span> <span class="n">cell_size</span><span class="p">))</span> + + <span class="n">pygame</span><span class="o">.</span><span class="n">display</span><span class="o">.</span><span class="n">flip</span><span class="p">()</span> +</code></pre></div> + +<p>Kết&nbsp;quả:</p> +<p><img alt="8queenpygame" src="https://n.pymi.vn/images/z38queenpygame.png"></p> +<p>full code tại <a href="https://n.pymi.vn/z38qgame.py">đây</a></p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Giải bài toán 8 quân hậu bằng Z32022-01-22T00:00:00+07:002022-01-22T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-01-22:/z38q.html<p><img alt="img" src="https://images.unsplash.com/photo-1571434798385-3c8988ae2f65?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDI4MzM2OTk&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p><a href="https://en.wikipedia.org/wiki/Eight_queens_puzzle">Bài toán 8 quân hậu</a>: bàn cờ vua kích thước 8x8, có 8 quân hậu, tìm vị trí sao cho 8 con không ăn được nhau (không cùng hàng ngang, cột, chéo). Bài toán này được giải bằng <a href="https://en.wikipedia.org/wiki/Backtracking">thuật toán backtracking</a>.</p> +<p>Backtracking giải bài này là thử đặt 1 quân …</p><p><img alt="img" src="https://images.unsplash.com/photo-1571434798385-3c8988ae2f65?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDI4MzM2OTk&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p><a href="https://en.wikipedia.org/wiki/Eight_queens_puzzle">Bài toán 8 quân hậu</a>: bàn cờ vua kích thước 8x8, có 8 quân hậu, tìm vị trí sao cho 8 con không ăn được nhau (không cùng hàng ngang, cột, chéo). Bài toán này được giải bằng <a href="https://en.wikipedia.org/wiki/Backtracking">thuật toán backtracking</a>.</p> +<p>Backtracking giải bài này là thử đặt 1 quân hậu ở vị trí bất kỳ, và tiếp tục đặt quân tiếp theo sao cho không ăn được quân trước, cứ tiếp tục cho đến khi tìm ra 8 vị trí. Nếu sau một số bước, thấy không thể đặt được quân ở vị trí nào phù hợp thì quay trở lại để thay đổi vị trí của bước trước đó rồi tiếp tục - nên gọi là&nbsp;backtracking.</p> +<p>Khi biểu diễn bài toán, có 2&nbsp;cách:</p> +<ul> +<li>tìm 8 cặp điểm (16 biến), ứng với tọa độ của từng quân&nbsp;hậu</li> +<li>gọi q1,q2, &#8230; q8 là các quân hậu ở cột 1-8, việc còn lại là tìm vị trí hàng của mỗi quân. Ở đây dùng cách&nbsp;này.</li> +</ul> +<p>Tạo 8&nbsp;biến:</p> +<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">z3</span> <span class="kn">import</span> <span class="o">*</span> +<span class="n">queens</span> <span class="o">=</span> <span class="p">[</span><span class="n">Int</span><span class="p">(</span><span class="sa">f</span><span class="s1">&#39;Q</span><span class="si">{</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="si">}</span><span class="s1">&#39;</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">8</span><span class="p">)]</span> +<span class="n">pp</span><span class="p">(</span><span class="n">queens</span><span class="p">)</span> +<span class="c1"># [Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8]</span> +</code></pre></div> + +<p>Điều kiện để các quân hậu nằm trong bàn&nbsp;cờ:</p> +<div class="highlight"><pre><span></span><code><span class="n">rows</span> <span class="o">=</span> <span class="p">[</span><span class="n">And</span><span class="p">(</span><span class="mi">1</span> <span class="o">&lt;=</span> <span class="n">q</span><span class="p">,</span> <span class="n">q</span> <span class="o">&lt;=</span><span class="mi">8</span><span class="p">)</span> <span class="k">for</span> <span class="n">q</span> <span class="ow">in</span> <span class="n">queens</span><span class="p">]</span> +<span class="n">pp</span><span class="p">(</span><span class="n">rows</span><span class="p">)</span> + +<span class="c1"># output</span> +<span class="p">[</span><span class="n">And</span><span class="p">(</span><span class="n">Q1</span> <span class="o">&gt;=</span> <span class="mi">1</span><span class="p">,</span> <span class="n">Q1</span> <span class="o">&lt;=</span> <span class="mi">8</span><span class="p">),</span> + <span class="n">And</span><span class="p">(</span><span class="n">Q2</span> <span class="o">&gt;=</span> <span class="mi">1</span><span class="p">,</span> <span class="n">Q2</span> <span class="o">&lt;=</span> <span class="mi">8</span><span class="p">),</span> + <span class="n">And</span><span class="p">(</span><span class="n">Q3</span> <span class="o">&gt;=</span> <span class="mi">1</span><span class="p">,</span> <span class="n">Q3</span> <span class="o">&lt;=</span> <span class="mi">8</span><span class="p">),</span> + <span class="n">And</span><span class="p">(</span><span class="n">Q4</span> <span class="o">&gt;=</span> <span class="mi">1</span><span class="p">,</span> <span class="n">Q4</span> <span class="o">&lt;=</span> <span class="mi">8</span><span class="p">),</span> + <span class="n">And</span><span class="p">(</span><span class="n">Q5</span> <span class="o">&gt;=</span> <span class="mi">1</span><span class="p">,</span> <span class="n">Q5</span> <span class="o">&lt;=</span> <span class="mi">8</span><span class="p">),</span> + <span class="n">And</span><span class="p">(</span><span class="n">Q6</span> <span class="o">&gt;=</span> <span class="mi">1</span><span class="p">,</span> <span class="n">Q6</span> <span class="o">&lt;=</span> <span class="mi">8</span><span class="p">),</span> + <span class="n">And</span><span class="p">(</span><span class="n">Q7</span> <span class="o">&gt;=</span> <span class="mi">1</span><span class="p">,</span> <span class="n">Q7</span> <span class="o">&lt;=</span> <span class="mi">8</span><span class="p">),</span> + <span class="n">And</span><span class="p">(</span><span class="n">Q8</span> <span class="o">&gt;=</span> <span class="mi">1</span><span class="p">,</span> <span class="n">Q8</span> <span class="o">&lt;=</span> <span class="mi">8</span><span class="p">)]</span> +</code></pre></div> + +<p>Thêm điều kiện để các quân ở các dòng riêng biệt&nbsp;&#8220;distinct&#8221;:</p> +<div class="highlight"><pre><span></span><code><span class="n">distinct</span> <span class="o">=</span> <span class="p">[</span><span class="n">Distinct</span><span class="p">(</span><span class="n">queens</span><span class="p">)]</span> +<span class="n">pp</span><span class="p">(</span><span class="n">distinct</span><span class="p">)</span> +<span class="c1"># [Distinct(Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8)]</span> +</code></pre></div> + +<p>Cuối cùng, thêm điều kiện để mỗi quân sau không cùng hàng chéo với quân trước. Hai quân cùng hàng chéo khi giá trị truyệt đối: |x2-x1| == |y2-y1|. Do ta đã mô hình cột (y) vào tên các biến, điều kiện ở đây sẽ là: +|q2 - q1| != 1, |q3-q1| != 2, |q3-q2| != 1,&nbsp;&#8230;</p> +<p>do Z3 không có function abs, có thể viết thành 2 điều kiện: +q2 - q1 != 1 và q2 - q1 !=&nbsp;-1.</p> +<div class="highlight"><pre><span></span><code><span class="n">diags</span> <span class="o">=</span> <span class="p">[</span><span class="n">And</span><span class="p">(</span><span class="n">queens</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">-</span> <span class="n">queens</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="o">-</span> <span class="n">j</span><span class="p">,</span> + <span class="n">queens</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">-</span> <span class="n">queens</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">!=</span> <span class="n">j</span> <span class="o">-</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="nb">range</span><span class="p">(</span><span class="mi">8</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="n">i</span><span class="p">)]</span> +</code></pre></div> + +<p>Giải tìm 1 nghiệm của bài&nbsp;toán:</p> +<div class="highlight"><pre><span></span><code><span class="n">solver</span> <span class="o">=</span> <span class="n">Solver</span><span class="p">()</span> +<span class="n">solver</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">columns</span><span class="o">+</span> <span class="n">distinct</span> <span class="o">+</span> <span class="n">diags</span><span class="p">)</span> +<span class="n">solver</span><span class="o">.</span><span class="n">check</span><span class="p">()</span> +<span class="n">m</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">model</span><span class="p">()</span> +<span class="n">pp</span><span class="p">(</span><span class="n">m</span><span class="p">)</span> +<span class="c1"># [Q2 = 4, Q8 = 3, Q5 = 5, Q6 = 7, Q4 = 8, Q7 = 1, Q1 = 6, Q3 = 2]</span> +</code></pre></div> + +<p>Vẽ lên để kiểm&nbsp;tra:</p> +<div class="highlight"><pre><span></span><code><span class="n">d</span> <span class="o">=</span> <span class="p">{</span><span class="n">i</span><span class="o">.</span><span class="n">name</span><span class="p">():</span> <span class="n">m</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">.</span><span class="n">as_long</span><span class="p">()</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">m</span><span class="p">}</span> + +<span class="k">for</span> <span class="n">row</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="k">for</span> <span class="n">col</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="k">if</span> <span class="n">row</span><span class="o">+</span><span class="mi">1</span> <span class="o">==</span> <span class="n">d</span><span class="p">[</span><span class="sa">f</span><span class="s1">&#39;Q</span><span class="si">{</span><span class="n">col</span><span class="o">+</span><span class="mi">1</span><span class="si">}</span><span class="s1">&#39;</span><span class="p">]:</span> + <span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s1">&#39;Q</span><span class="si">{</span><span class="n">col</span><span class="o">+</span><span class="mi">1</span><span class="si">}</span><span class="s1">&#39;</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="s2">&quot; &quot;</span><span class="p">)</span> + <span class="k">else</span><span class="p">:</span> + <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;__&quot;</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="s2">&quot; &quot;</span><span class="p">)</span> + <span class="nb">print</span><span class="p">()</span> +</code></pre></div> + +<div class="highlight"><pre><span></span><code>__ __ __ __ __ __ Q7 __ +__ __ Q3 __ __ __ __ __ +__ __ __ __ __ __ __ Q8 +__ Q2 __ __ __ __ __ __ +__ __ __ __ Q5 __ __ __ +Q1 __ __ __ __ __ __ __ +__ __ __ __ __ Q6 __ __ +__ __ __ Q4 __ __ __ __ +</code></pre></div> + +<p>Toàn bộ&nbsp;code:</p> +<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">z3</span> <span class="kn">import</span> <span class="o">*</span> +<span class="n">queens</span> <span class="o">=</span> <span class="p">[</span><span class="n">Int</span><span class="p">(</span><span class="sa">f</span><span class="s1">&#39;Q</span><span class="si">{</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="si">}</span><span class="s1">&#39;</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">8</span><span class="p">)]</span> +<span class="n">pp</span><span class="p">(</span><span class="n">queens</span><span class="p">)</span> + +<span class="n">columns</span> <span class="o">=</span> <span class="p">[</span><span class="n">And</span><span class="p">(</span><span class="mi">1</span> <span class="o">&lt;=</span> <span class="n">q</span><span class="p">,</span> <span class="n">q</span> <span class="o">&lt;=</span><span class="mi">8</span><span class="p">)</span> <span class="k">for</span> <span class="n">q</span> <span class="ow">in</span> <span class="n">queens</span><span class="p">]</span> +<span class="n">pp</span><span class="p">(</span><span class="n">columns</span><span class="p">)</span> + +<span class="n">distinct</span> <span class="o">=</span> <span class="p">[</span><span class="n">Distinct</span><span class="p">(</span><span class="n">queens</span><span class="p">)]</span> +<span class="n">pp</span><span class="p">(</span><span class="n">distinct</span><span class="p">)</span> + +<span class="n">diags</span> <span class="o">=</span> <span class="p">[</span><span class="n">And</span><span class="p">(</span><span class="n">queens</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">-</span> <span class="n">queens</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="o">-</span> <span class="n">j</span><span class="p">,</span> + <span class="n">queens</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">-</span> <span class="n">queens</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">!=</span> <span class="n">j</span> <span class="o">-</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="nb">range</span><span class="p">(</span><span class="mi">8</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="n">i</span><span class="p">)]</span> + +<span class="n">solver</span> <span class="o">=</span> <span class="n">Solver</span><span class="p">()</span> +<span class="n">solver</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">columns</span><span class="o">+</span> <span class="n">distinct</span> <span class="o">+</span> <span class="n">diags</span><span class="p">)</span> +<span class="n">solver</span><span class="o">.</span><span class="n">check</span><span class="p">()</span> +<span class="n">m</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">model</span><span class="p">()</span> +<span class="n">pp</span><span class="p">(</span><span class="n">m</span><span class="p">)</span> +</code></pre></div> + +<p>Tham khảo: <a href="https://ericpony.github.io/z3py-tutorial/guide-examples.htm">https://ericpony.github.io/z3py-tutorial/guide-examples.htm</a></p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Z3 nhanh chậm ra sao?2022-01-20T00:00:00+07:002022-01-20T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-01-20:/z3speed.html<p><img alt="img" src="https://images.unsplash.com/photo-1630071634094-64b2d5b40c57?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDI2ODk5MjM&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Z3 giải toán vi diệu là vậy, nhưng mỗi lần tìm ra 1 nghiệm thôi, vậy tìm 1000 nghiệm của bất phương trình 0 &lt; x &lt; 1001 thì mất bao&nbsp;lâu?</p> +<p>Cách làm tương tự <a href="https://n.pymi.vn/z3ineq.html">phần trước</a>, ta tìm ra 1 nghiệm rồi thêm nghiệm đó vào điều kiện loại&nbsp;trừ …</p><p><img alt="img" src="https://images.unsplash.com/photo-1630071634094-64b2d5b40c57?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDI2ODk5MjM&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Z3 giải toán vi diệu là vậy, nhưng mỗi lần tìm ra 1 nghiệm thôi, vậy tìm 1000 nghiệm của bất phương trình 0 &lt; x &lt; 1001 thì mất bao&nbsp;lâu?</p> +<p>Cách làm tương tự <a href="https://n.pymi.vn/z3ineq.html">phần trước</a>, ta tìm ra 1 nghiệm rồi thêm nghiệm đó vào điều kiện loại&nbsp;trừ.</p> +<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">z3</span> <span class="kn">import</span> <span class="o">*</span> +<span class="n">solver</span> <span class="o">=</span> <span class="n">Solver</span><span class="p">()</span> +<span class="n">x</span> <span class="o">=</span> <span class="n">Int</span><span class="p">(</span><span class="s1">&#39;x&#39;</span><span class="p">)</span> +<span class="n">solver</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">x</span> <span class="o">&lt;</span> <span class="mi">1001</span><span class="p">,</span> <span class="n">x</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> + +<span class="k">while</span> <span class="n">solver</span><span class="o">.</span><span class="n">check</span><span class="p">()</span> <span class="o">==</span> <span class="n">sat</span><span class="p">:</span> + <span class="n">model</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">model</span><span class="p">()</span> + <span class="n">var</span> <span class="o">=</span> <span class="n">model</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> + <span class="n">solver</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">var</span><span class="p">()</span> <span class="o">!=</span> <span class="n">model</span><span class="p">[</span><span class="n">var</span><span class="p">()])</span> +</code></pre></div> + +<div class="highlight"><pre><span></span><code>$ <span class="nb">time</span> python3 z3speed.py + +real 0m1.508s +</code></pre></div> + +<p>mất 1.5 giây. Trong khi với 1.5 giây, chương trình Python đơn giản có thể thêm 14 triệu phần tử vào&nbsp;list:</p> +<div class="highlight"><pre><span></span><code><span class="nv">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="w"></span> +<span class="nv">xs</span><span class="w"> </span><span class="o">=</span><span class="w"> </span>[]<span class="w"></span> +<span class="k">while</span><span class="w"> </span><span class="nv">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="mi">14</span><span class="nv">_000_000</span>:<span class="w"></span> +<span class="w"> </span><span class="nv">xs</span>.<span class="nv">append</span><span class="ss">(</span><span class="nv">i</span><span class="ss">)</span><span class="w"></span> +<span class="w"> </span><span class="nv">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">i</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="w"></span> +</code></pre></div> + +<p>Kết luận: tốc độ tìm ra 1 nghiệm của Z3 không nhanh, so với append list chậm hơn 14 nghìn&nbsp;lần.</p> +<p>Thử tăng số lên&nbsp;2001,</p> +<div class="highlight"><pre><span></span><code>$ <span class="nb">time</span> python3 z3speed.py + +real 0m5.499s +user 0m5.460s +</code></pre></div> + +<p>Chậm gấp &gt; 3 lần, chứ không phải 2 lần. Và nếu tăng lên&nbsp;10001,</p> +<div class="highlight"><pre><span></span><code>$ <span class="nb">time</span> python3 z3speed.py + +real 3m33.323s +user 3m32.711s +sys 0m0.216s +</code></pre></div> + +<p>~ 210&nbsp;giây.</p> +<p><img alt="speed" src="https://n.pymi.vn/images/z3speed.png"></p> +<p>Vậy việc mang Z3 đi đếm số nghiệm (450k) của <a href="https://n.pymi.vn/z3grade3.html">bài toán lớp +3</a> có vẻ như không khả&nbsp;thi.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Tìm tất cả nghiệm của bất phương trình bằng Z32022-01-18T00:00:00+07:002022-01-18T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-01-18:/z3ineq.html<p><img alt="img" src="https://images.unsplash.com/photo-1542805700-2fadb851b97a?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDI1MTA1ODA&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Các bài trước dùng Z3 để trả về 1 nghiệm của bài toán (do Z3 quyết định), lần này +ta sẽ viết thêm code để khiến Z3 tìm thêm các nghiệm cho tới khi đủ&nbsp;nghiệm.</p> +<p>Cho bất phương trình: x + y &lt; 4, với x, y nguyên dương. +Dễ mò …</p><p><img alt="img" src="https://images.unsplash.com/photo-1542805700-2fadb851b97a?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDI1MTA1ODA&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Các bài trước dùng Z3 để trả về 1 nghiệm của bài toán (do Z3 quyết định), lần này +ta sẽ viết thêm code để khiến Z3 tìm thêm các nghiệm cho tới khi đủ&nbsp;nghiệm.</p> +<p>Cho bất phương trình: x + y &lt; 4, với x, y nguyên dương. +Dễ mò được có 3 nghiệm: 1 1, 1 2, 2&nbsp;1.</p> +<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">z3</span> <span class="kn">import</span> <span class="o">*</span> + +<span class="n">solver</span> <span class="o">=</span> <span class="n">Solver</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">Ints</span><span class="p">(</span><span class="s1">&#39;x y&#39;</span><span class="p">)</span> + +<span class="n">solver</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">x</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> +<span class="n">solver</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">y</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> +<span class="n">solver</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="n">y</span> <span class="o">&lt;</span> <span class="mi">4</span><span class="p">)</span> +<span class="n">solver</span><span class="o">.</span><span class="n">check</span><span class="p">()</span> <span class="o">==</span> <span class="n">sat</span> +<span class="nb">print</span><span class="p">(</span><span class="n">solver</span><span class="o">.</span><span class="n">model</span><span class="p">())</span> +</code></pre></div> + +<p>[x = 1, y =&nbsp;1]</p> +<p>Để tìm ra tất cả các nghiệm, cần viết thêm code để sau khi giải ra 1 nghiệm, +đưa thêm vào solver một dữ kiện là nghiệm cần tìm tiếp theo phải khác nghiệm +vừa tìm&nbsp;được.</p> +<p>Tức thêm vào: <code>x != 1 or y != 1</code> +Để viết or như vậy, trong Z3 viết: <code>Or(list)</code> hay ở đây là <code>Or([x != 1, y != 1])</code></p> +<p>thay vì phải tự gõ x, y, và các giá trị của nghiệm vừa tìm được,&nbsp;dùng:</p> +<p><code>for var in model</code> sẽ trả về x và y khi gọi <code>var()</code>, sẽ có giá trị nghiệm tương +ứng khi viết <code>model[x]</code> và <code>model[y]</code></p> +<div class="highlight"><pre><span></span><code><span class="n">solver</span> <span class="o">=</span> <span class="n">Solver</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">Ints</span><span class="p">(</span><span class="s1">&#39;x y&#39;</span><span class="p">)</span> + +<span class="n">solver</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">x</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> +<span class="n">solver</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">y</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> +<span class="n">solver</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="n">y</span> <span class="o">&lt;</span> <span class="mi">4</span><span class="p">)</span> +<span class="k">while</span> <span class="n">solver</span><span class="o">.</span><span class="n">check</span><span class="p">()</span> <span class="o">==</span> <span class="n">sat</span><span class="p">:</span> + <span class="n">model</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">model</span><span class="p">()</span> + <span class="nb">print</span><span class="p">(</span><span class="n">model</span><span class="p">)</span> + <span class="n">block</span> <span class="o">=</span> <span class="p">[]</span> + <span class="k">for</span> <span class="n">var</span> <span class="ow">in</span> <span class="n">model</span><span class="p">:</span> + <span class="n">symbol</span> <span class="o">=</span> <span class="n">var</span><span class="p">()</span> + <span class="n">value</span> <span class="o">=</span> <span class="n">model</span><span class="p">[</span><span class="n">symbol</span><span class="p">]</span> + <span class="n">block</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">symbol</span> <span class="o">!=</span> <span class="n">value</span><span class="p">)</span> + + <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Blocking: &quot;</span><span class="p">,</span> <span class="n">Or</span><span class="p">(</span><span class="n">block</span><span class="p">))</span> + <span class="n">solver</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">Or</span><span class="p">(</span><span class="n">block</span><span class="p">))</span> +</code></pre></div> + +<p>Kết quả được 3&nbsp;nghiệm:</p> +<div class="highlight"><pre><span></span><code><span class="k">[x = 1, y = 1]</span><span class="w"></span> +<span class="na">Blocking: Or(1 !</span><span class="o">=</span><span class="w"> </span><span class="s">x, 1 != y)</span><span class="w"></span> +<span class="k">[x = 1, y = 2]</span><span class="w"></span> +<span class="na">Blocking: Or(1 !</span><span class="o">=</span><span class="w"> </span><span class="s">x, 2 != y)</span><span class="w"></span> +<span class="k">[x = 2, y = 1]</span><span class="w"></span> +<span class="na">Blocking: Or(2 !</span><span class="o">=</span><span class="w"> </span><span class="s">x, 1 != y)</span><span class="w"></span> +</code></pre></div> + +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Giải “Bài toán lớp 3 có số lượng đáp án khổng lồ” bằng Z32022-01-16T00:00:00+07:002022-01-16T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-01-16:/z3grade3.html<p><a href="https://www.familug.org/2015/05/codegolf-giai-bai-toan-lop-3-co-so.html">Bài toán lớp 3</a>:</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1539213492139-7b268eb93c82?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDIzMjY0Nzg&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<div class="highlight"><pre><span></span><code>a + 13 * b / c + d + 12 * e – f – 11 + g * h / i – 10 = 66 +</code></pre></div> + +<p><img alt="lop3" src="https://3.bp.blogspot.com/-JbRWh5-nuHw/VVzPv0QAE0I/AAAAAAAATXs/35mGvnHLS3g/s320/baitoan1-2539-1431999391.jpg"></p> +<p>Tìm các số nguyên dương &lt; 10, a đến i thoả mãn bài toán (tìm một nghiệm của bài&nbsp;toán)</p> +<p>Giải bằng&nbsp;Z3:</p> +<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">z3</span> <span class="kn">import</span> <span class="n">solve</span><span class="p">,</span> <span class="n">Ints</span> +<span class="n">a</span><span class="p">,</span><span class="n">b</span><span class="p">,</span><span class="n">c</span><span class="p">,</span><span class="n">d</span><span class="p">,</span><span class="n">e …</span></code></pre></div><p><a href="https://www.familug.org/2015/05/codegolf-giai-bai-toan-lop-3-co-so.html">Bài toán lớp 3</a>:</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1539213492139-7b268eb93c82?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDIzMjY0Nzg&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<div class="highlight"><pre><span></span><code>a + 13 * b / c + d + 12 * e – f – 11 + g * h / i – 10 = 66 +</code></pre></div> + +<p><img alt="lop3" src="https://3.bp.blogspot.com/-JbRWh5-nuHw/VVzPv0QAE0I/AAAAAAAATXs/35mGvnHLS3g/s320/baitoan1-2539-1431999391.jpg"></p> +<p>Tìm các số nguyên dương &lt; 10, a đến i thoả mãn bài toán (tìm một nghiệm của bài&nbsp;toán)</p> +<p>Giải bằng&nbsp;Z3:</p> +<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">z3</span> <span class="kn">import</span> <span class="n">solve</span><span class="p">,</span> <span class="n">Ints</span> +<span class="n">a</span><span class="p">,</span><span class="n">b</span><span class="p">,</span><span class="n">c</span><span class="p">,</span><span class="n">d</span><span class="p">,</span><span class="n">e</span><span class="p">,</span><span class="n">f</span><span class="p">,</span><span class="n">g</span><span class="p">,</span><span class="n">h</span><span class="p">,</span><span class="n">i</span> <span class="o">=</span> <span class="n">Ints</span><span class="p">(</span><span class="s1">&#39;a b c d e f g h i&#39;</span><span class="p">)</span> +<span class="n">solve</span><span class="p">(</span> <span class="n">a</span> <span class="o">+</span> <span class="mi">13</span> <span class="o">*</span> <span class="n">b</span> <span class="o">/</span> <span class="n">c</span> <span class="o">+</span> <span class="n">d</span> <span class="o">+</span> <span class="mi">12</span> <span class="o">*</span> <span class="n">e</span> <span class="o">-</span> <span class="n">f</span> <span class="o">-</span> <span class="mi">11</span> <span class="o">+</span> <span class="n">g</span> <span class="o">*</span> <span class="n">h</span> <span class="o">/</span> <span class="n">i</span> <span class="o">-</span> <span class="mi">10</span> <span class="o">==</span> <span class="mi">66</span><span class="p">)</span> +</code></pre></div> + +<p>Thấy kết quả sai, do chưa có điều kiện giới hạn khoảng giá trị cho&nbsp;a,b,c,d,e,f,g,h,i.</p> +<div class="highlight"><pre><span></span><code>[a = 87, + b = 0, + c = 0, + d = 0, + e = 0, + f = 0, + g = 0, + i = 0, + h = 1] +</code></pre></div> + +<p>Thêm các điều kiện&nbsp;vào:</p> +<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">z3</span> <span class="kn">import</span> <span class="n">solve</span><span class="p">,</span> <span class="n">Ints</span> +<span class="n">a</span><span class="p">,</span><span class="n">b</span><span class="p">,</span><span class="n">c</span><span class="p">,</span><span class="n">d</span><span class="p">,</span><span class="n">e</span><span class="p">,</span><span class="n">f</span><span class="p">,</span><span class="n">g</span><span class="p">,</span><span class="n">h</span><span class="p">,</span><span class="n">i</span> <span class="o">=</span> <span class="n">Ints</span><span class="p">(</span><span class="s1">&#39;a b c d e f g h i&#39;</span><span class="p">)</span> +<span class="n">solve</span><span class="p">(</span> +<span class="mi">0</span><span class="o">&lt;</span> <span class="n">a</span><span class="p">,</span> <span class="n">a</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="p">,</span> +<span class="mi">0</span><span class="o">&lt;</span> <span class="n">b</span><span class="p">,</span> <span class="n">b</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="p">,</span> +<span class="mi">0</span><span class="o">&lt;</span> <span class="n">c</span><span class="p">,</span> <span class="n">c</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="p">,</span> +<span class="mi">0</span><span class="o">&lt;</span> <span class="n">d</span><span class="p">,</span> <span class="n">d</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="p">,</span> +<span class="mi">0</span><span class="o">&lt;</span> <span class="n">e</span><span class="p">,</span> <span class="n">e</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="p">,</span> +<span class="mi">0</span><span class="o">&lt;</span> <span class="n">f</span><span class="p">,</span> <span class="n">f</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="p">,</span> +<span class="mi">0</span><span class="o">&lt;</span> <span class="n">g</span><span class="p">,</span> <span class="n">g</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="p">,</span> +<span class="mi">0</span><span class="o">&lt;</span> <span class="n">h</span><span class="p">,</span> <span class="n">h</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="p">,</span> +<span class="mi">0</span><span class="o">&lt;</span> <span class="n">i</span><span class="p">,</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="p">,</span> +<span class="n">a</span> <span class="o">+</span> <span class="mi">13</span> <span class="o">*</span> <span class="n">b</span> <span class="o">/</span> <span class="n">c</span> <span class="o">+</span> <span class="n">d</span> <span class="o">+</span> <span class="mi">12</span> <span class="o">*</span> <span class="n">e</span> <span class="o">-</span> <span class="n">f</span> <span class="o">-</span> <span class="mi">11</span> <span class="o">+</span> <span class="n">g</span> <span class="o">*</span> <span class="n">h</span> <span class="o">/</span> <span class="n">i</span> <span class="o">-</span> <span class="mi">10</span> <span class="o">==</span> <span class="mi">66</span><span class="p">)</span> +</code></pre></div> + +<p>Kết&nbsp;quả</p> +<div class="highlight"><pre><span></span><code><span class="p">[</span><span class="n">f</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">5</span><span class="p">,</span><span class="w"></span> +<span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"></span> +<span class="w"> </span><span class="n">g</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">7</span><span class="p">,</span><span class="w"></span> +<span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"></span> +<span class="w"> </span><span class="n">d</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">5</span><span class="p">,</span><span class="w"></span> +<span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"></span> +<span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"></span> +<span class="w"> </span><span class="n">h</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">7</span><span class="p">,</span><span class="w"></span> +<span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"></span> +<span class="w"> </span><span class="n">div0</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[(</span><span class="mi">49</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="mi">49</span><span class="p">,</span><span class="w"> </span><span class="n">else</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="mi">13</span><span class="p">],</span><span class="w"></span> +<span class="w"> </span><span class="n">mod0</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[</span><span class="n">else</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="mi">0</span><span class="p">]]</span><span class="w"></span> +</code></pre></div> + +<p>Dù ở đây đã tìm được ra 1 nghiệm, nhưng cách gọi solve() khá giới hạn do phải liệt kê hết các điều kiện cùng 1 lúc. Thay vì gọi trực tiếp solve, tạo 1 object &#8220;solver&#8221; rồi add các điều kiện vào, kiểm tra và nhận về&nbsp;&#8220;model&#8221;:</p> +<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">z3</span> <span class="kn">import</span> <span class="n">Solver</span><span class="p">,</span> <span class="n">Ints</span> +<span class="nb">vars</span> <span class="o">=</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">c</span><span class="p">,</span><span class="n">d</span><span class="p">,</span><span class="n">e</span><span class="p">,</span><span class="n">f</span><span class="p">,</span><span class="n">g</span><span class="p">,</span><span class="n">h</span><span class="p">,</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">Ints</span><span class="p">(</span><span class="s1">&#39;a b c d e f g h i&#39;</span><span class="p">)</span> +<span class="n">solver</span> <span class="o">=</span> <span class="n">Solver</span><span class="p">()</span> +<span class="n">solver</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">a</span> <span class="o">+</span> <span class="mi">13</span> <span class="o">*</span> <span class="n">b</span> <span class="o">/</span> <span class="n">c</span> <span class="o">+</span> <span class="n">d</span> <span class="o">+</span> <span class="mi">12</span> <span class="o">*</span> <span class="n">e</span> <span class="o">-</span> <span class="n">f</span> <span class="o">-</span> <span class="mi">11</span> <span class="o">+</span> <span class="n">g</span> <span class="o">*</span> <span class="n">h</span> <span class="o">/</span> <span class="n">i</span> <span class="o">-</span> <span class="mi">10</span> <span class="o">==</span> <span class="mi">66</span><span class="p">)</span> +<span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="nb">vars</span><span class="p">:</span> + <span class="n">solver</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="mi">0</span> <span class="o">&lt;</span> <span class="n">v</span><span class="p">)</span> + <span class="n">solver</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">v</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="p">)</span> + +<span class="nb">print</span><span class="p">(</span><span class="n">solver</span><span class="o">.</span><span class="n">check</span><span class="p">())</span> +<span class="n">m</span> <span class="o">=</span> <span class="n">solver</span><span class="o">.</span><span class="n">model</span><span class="p">()</span> +<span class="nb">print</span><span class="p">(</span><span class="nb">type</span><span class="p">(</span><span class="n">m</span><span class="p">),</span> <span class="n">m</span><span class="p">)</span> +<span class="nb">print</span><span class="p">(</span><span class="n">m</span><span class="o">.</span><span class="n">eval</span><span class="p">((</span><span class="n">a</span> <span class="o">+</span> <span class="mi">13</span> <span class="o">*</span> <span class="n">b</span> <span class="o">/</span> <span class="n">c</span> <span class="o">+</span> <span class="n">d</span> <span class="o">+</span> <span class="mi">12</span> <span class="o">*</span> <span class="n">e</span> <span class="o">-</span> <span class="n">f</span> <span class="o">-</span> <span class="mi">11</span> <span class="o">+</span> <span class="n">g</span> <span class="o">*</span> <span class="n">h</span> <span class="o">/</span> <span class="n">i</span> <span class="o">-</span> <span class="mi">10</span><span class="p">)</span> <span class="o">==</span> <span class="mi">66</span><span class="p">))</span> +</code></pre></div> + +<p>Kết&nbsp;quả:</p> +<div class="highlight"><pre><span></span><code><span class="n">sat</span><span class="w"></span> +<span class="o">&lt;</span><span class="n">class</span><span class="w"> </span><span class="s">&#39;z3.z3.ModelRef&#39;</span><span class="o">&gt;</span><span class="w"> </span><span class="p">[</span><span class="n">f</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">9</span><span class="p">,</span><span class="w"></span> +<span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">7</span><span class="p">,</span><span class="w"></span> +<span class="w"> </span><span class="n">g</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"></span> +<span class="w"> </span><span class="n">c</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"></span> +<span class="w"> </span><span class="n">d</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">8</span><span class="p">,</span><span class="w"></span> +<span class="w"> </span><span class="n">e</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">4</span><span class="p">,</span><span class="w"></span> +<span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"></span> +<span class="w"> </span><span class="n">h</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"></span> +<span class="w"> </span><span class="n">b</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">3</span><span class="p">,</span><span class="w"></span> +<span class="w"> </span><span class="n">div0</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[(</span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">7</span><span class="p">)</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="n">else</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="mi">39</span><span class="p">],</span><span class="w"></span> +<span class="w"> </span><span class="n">mod0</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">[(</span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="mi">7</span><span class="p">)</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="n">else</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="mi">0</span><span class="p">]]</span><span class="w"></span> +<span class="kr">True</span><span class="w"></span> +</code></pre></div> + +<p>Khi gọi <code>solver.check()</code>, nó sẽ tìm một &#8220;kết quả&#8221; và trả về &#8220;sat&#8221; (satisfy - thỏa mãn), gọi <code>solver.model()</code> trả về 1 model (nghiệm) của bài toán. <code>m.eval()</code> thay các giá trị trong nghiệm vào biểu thức và tính ra vế trái bằng vế phải bằng&nbsp;66.</p> +<p>Phần sau sẽ dùng Z3 để tìm tất cả các nghiệm bài toán&nbsp;này.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>k in dict.keys() chậm hơn k in dict bao nhiêu?2022-01-15T00:00:00+07:002022-01-15T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-01-15:/dictkey.html<p><img alt="img" src="https://images.unsplash.com/photo-1517331237869-8c4ed7fadcb4?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDIyMjQ5ODg&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Python2, khi gọi dict.keys() sẽ trả về 1 list các key của dict. Một cải tiến +lớn của Python3 là không trả về list mà trả về những thứ tương tự +<a href="https://pp.pymi.vn/article/tuple_comps/">generator</a> để tiết kiệm bộ&nbsp;nhớ.</p> +<div class="highlight"><pre><span></span><code><span class="n">In</span> <span class="p">[</span><span class="mi">32</span><span class="p">]:</span> <span class="nb">type</span><span class="p">({}</span><span class="o">.</span><span class="n">keys</span><span class="p">())</span> +<span class="n">Out</span><span class="p">[</span><span class="mi">32</span><span class="p">]:</span> <span class="n">dict_keys</span> + +<span class="n">In</span> <span class="p">[</span><span class="mi">33</span><span class="p">]:</span> <span class="nb">type</span><span class="p">({}</span><span class="o">.</span><span class="n">items</span><span class="p">())</span> +<span class="n">Out …</span></code></pre></div><p><img alt="img" src="https://images.unsplash.com/photo-1517331237869-8c4ed7fadcb4?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDIyMjQ5ODg&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Python2, khi gọi dict.keys() sẽ trả về 1 list các key của dict. Một cải tiến +lớn của Python3 là không trả về list mà trả về những thứ tương tự +<a href="https://pp.pymi.vn/article/tuple_comps/">generator</a> để tiết kiệm bộ&nbsp;nhớ.</p> +<div class="highlight"><pre><span></span><code><span class="n">In</span> <span class="p">[</span><span class="mi">32</span><span class="p">]:</span> <span class="nb">type</span><span class="p">({}</span><span class="o">.</span><span class="n">keys</span><span class="p">())</span> +<span class="n">Out</span><span class="p">[</span><span class="mi">32</span><span class="p">]:</span> <span class="n">dict_keys</span> + +<span class="n">In</span> <span class="p">[</span><span class="mi">33</span><span class="p">]:</span> <span class="nb">type</span><span class="p">({}</span><span class="o">.</span><span class="n">items</span><span class="p">())</span> +<span class="n">Out</span><span class="p">[</span><span class="mi">33</span><span class="p">]:</span> <span class="n">dict_items</span> + +<span class="n">In</span> <span class="p">[</span><span class="mi">34</span><span class="p">]:</span> <span class="nb">type</span><span class="p">({}</span><span class="o">.</span><span class="n">values</span><span class="p">())</span> +<span class="n">Out</span><span class="p">[</span><span class="mi">34</span><span class="p">]:</span> <span class="n">dict_values</span> +</code></pre></div> + +<p>Khi nhìn <code>k in dict</code>, đó là phép toán có độ phức tạp O(1), nhanh tức thì, thì +có thể đoán <code>k in dict.keys()</code> sẽ tìm k trong 1 generator iterator (hay nôm na +là 1 list), sẽ rất chậm. Dùng timeit để&nbsp;thử:</p> +<div class="highlight"><pre><span></span><code><span class="c1"># import timeit</span> + +<span class="n">In</span> <span class="p">[</span><span class="mi">38</span><span class="p">]:</span> <span class="n">timeit</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="s1">&#39;-1 in d&#39;</span><span class="p">,</span> <span class="n">setup</span><span class="o">=</span><span class="s1">&#39;d = {i:i for i in range(30_000_000)}&#39;</span><span class="p">,</span> <span class="n">number</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span> +<span class="n">Out</span><span class="p">[</span><span class="mi">38</span><span class="p">]:</span> <span class="mf">2.053999196505174e-06</span> + +<span class="n">In</span> <span class="p">[</span><span class="mi">39</span><span class="p">]:</span> <span class="n">timeit</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="s1">&#39;-1 in d.keys()&#39;</span><span class="p">,</span> <span class="n">setup</span><span class="o">=</span><span class="s1">&#39;d = {i:i for i in range(30_000_000)}&#39;</span><span class="p">,</span> <span class="n">number</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span> +<span class="n">Out</span><span class="p">[</span><span class="mi">39</span><span class="p">]:</span> <span class="mf">5.510002665687352e-06</span> +</code></pre></div> + +<p>Thấy <code>k in d.keys()</code> chậm bằng nửa <code>k in d</code>, đúng là chậm hơn, nhưng chỉ 1 nửa. +Trong khi nếu là list, thì tìm kiếm phải <a href="https://n.pymi.vn/dictvslist.html">chậm hơn dict hàng nghìn +lần</a>.</p> +<p>Sự chênh lệch này hóa ra do gọi <code>d.keys()</code></p> +<div class="highlight"><pre><span></span><code><span class="n">In</span> <span class="p">[</span><span class="mi">40</span><span class="p">]:</span> <span class="n">timeit</span><span class="o">.</span><span class="n">timeit</span><span class="p">(</span><span class="s1">&#39;d.keys()&#39;</span><span class="p">,</span> <span class="n">setup</span><span class="o">=</span><span class="s1">&#39;d = {i:i for i in range(30_000_000)}&#39;</span><span class="p">,</span> <span class="n">number</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span> +<span class="n">Out</span><span class="p">[</span><span class="mi">40</span><span class="p">]:</span> <span class="mf">2.93600169243291e-06</span> +</code></pre></div> + +<p>Nếu trừ phần này, thì 2 đoạn code nhanh như nhau. Tại&nbsp;sao?</p> +<p>Lý do bởi Python3, <a href="https://docs.python.org/3/library/stdtypes.html#dictionary-view-objects">dict.keys() trả về key view</a>:</p> +<blockquote> +<p>The objects returned by dict.keys(), dict.values() and dict.items() are view objects. They provide a dynamic view on the dictionary’s entries, which means that when the dictionary changes, the view reflects these&nbsp;changes.</p> +</blockquote> +<p>Với chú ý keys view hoạt động như kiểu set chứ không phải list. Mà dict key hoạt động như set nên tốc độ là như&nbsp;nhau:</p> +<blockquote> +<p>Keys views are set-like since their entries are unique and hashable. If all values are hashable, so that (key, value) pairs are unique and hashable, then the items view is also set-like. (Values views are not treated as set-like since the entries are generally not&nbsp;unique.)</p> +</blockquote> +<p>Keys view dùng như&nbsp;set:</p> +<div class="highlight"><pre><span></span><code><span class="n">In</span> <span class="p">[</span><span class="mi">26</span><span class="p">]:</span> <span class="n">d</span> <span class="o">=</span> <span class="p">{</span><span class="n">i</span><span class="p">:</span><span class="n">i</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">5</span><span class="p">)}</span> + +<span class="n">In</span> <span class="p">[</span><span class="mi">27</span><span class="p">]:</span> <span class="n">d2</span> <span class="o">=</span> <span class="p">{</span><span class="n">i</span><span class="p">:</span><span class="n">i</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">4</span><span class="p">,</span><span class="mi">10</span><span class="p">)}</span> + +<span class="n">In</span> <span class="p">[</span><span class="mi">28</span><span class="p">]:</span> <span class="n">d</span><span class="o">.</span><span class="n">keys</span><span class="p">()</span> <span class="o">&amp;</span> <span class="n">d2</span><span class="o">.</span><span class="n">keys</span><span class="p">()</span> +<span class="n">Out</span><span class="p">[</span><span class="mi">28</span><span class="p">]:</span> <span class="p">{</span><span class="mi">4</span><span class="p">}</span> +</code></pre></div> + +<p>Dù vậy, viết <code>k in dict</code> vẫn là nhanh, ngắn&nbsp;nhất.</p> +<p>Xem:&nbsp;https://docs.python.org/3/library/stdtypes.html#dictionary-view-objects</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Giải toán lớp 4: 50 cặp chân gà, chó bằng Z32022-01-08T00:00:00+07:002022-01-08T00:00:00+07:00Pymier0tag:n.pymi.vn,2022-01-08:/z3p1.html<p><img alt="img" src="https://images.unsplash.com/photo-1582456313540-088dcd72e53a?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDE2NTg4NTc&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p><a href="https://github.com/Z3Prover/z3#python">Z3</a> là một &#8220;theorem prover&#8221;, thuộc dạng Satisfiability Modulo Theories (<span class="caps">SMT</span>) solver, một sản phẩm của <a href="https://theory.stanford.edu/~nikolaj/programmingz3.html">Microsoft Research</a>. Nó có thể làm rất nhiều thứ, một cách &#8220;magic&#8221;, không dễ để hiểu, nhưng không khó để&nbsp;dùng.</p> +<p>Loạt bài sẽ dùng Z3 để giải các bài toán đố chỉ bằng …</p><p><img alt="img" src="https://images.unsplash.com/photo-1582456313540-088dcd72e53a?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2NDE2NTg4NTc&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p><a href="https://github.com/Z3Prover/z3#python">Z3</a> là một &#8220;theorem prover&#8221;, thuộc dạng Satisfiability Modulo Theories (<span class="caps">SMT</span>) solver, một sản phẩm của <a href="https://theory.stanford.edu/~nikolaj/programmingz3.html">Microsoft Research</a>. Nó có thể làm rất nhiều thứ, một cách &#8220;magic&#8221;, không dễ để hiểu, nhưng không khó để&nbsp;dùng.</p> +<p>Loạt bài sẽ dùng Z3 để giải các bài toán đố chỉ bằng cách: mô tả bài&nbsp;toán.</p> +<p>Toán cấp 1 có một bài toán lừng danh ở dạng &#8220;tứ ngôn, tứ tuyệt&#8221;, như&nbsp;sau:</p> +<p><center> +Vừa gà, vừa&nbsp;chó</p> +<p>bó lại cho&nbsp;tròn</p> +<p>ba mươi sáu&nbsp;con</p> +<p>một trăm chân chẵn +</center></p> +<p>Hỏi có bao nhiêu con gà và chó. Và từ đó các cô các thầy sẽ dạy học sinh &#8220;phương pháp tìm hai số khi biết tổng và&nbsp;hiệu&#8221;&#8230;</p> +<p>Cài z3: <code>pip install z3-solver</code></p> +<p>Giải bằng&nbsp;Z3</p> +<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">z3</span> <span class="kn">import</span> <span class="n">Int</span><span class="p">,</span> <span class="n">solve</span> +<span class="n">ga</span> <span class="o">=</span> <span class="n">Int</span><span class="p">(</span><span class="s1">&#39;ga&#39;</span><span class="p">)</span> +<span class="n">cho</span> <span class="o">=</span> <span class="n">Int</span><span class="p">(</span><span class="s1">&#39;cho&#39;</span><span class="p">)</span> +<span class="n">solve</span><span class="p">(</span><span class="n">ga</span> <span class="o">+</span> <span class="n">cho</span> <span class="o">==</span> <span class="mi">36</span><span class="p">,</span> <span class="n">ga</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">+</span> <span class="n">cho</span> <span class="o">*</span> <span class="mi">4</span> <span class="o">==</span> <span class="mi">100</span><span class="p">)</span> +</code></pre></div> + +<p>Kết quả hiện&nbsp;ra: </p> +<blockquote> +<p>[cho = 14, ga =&nbsp;22]</p> +</blockquote> +<p>Ta mô hình bài toán bằng việc tạo 1 biến Int chứa số con gà, 1 biến Int khác chứa số con chó, gọi function giải: <strong>solve</strong> với lần lượt các điều kiện. Z3 giải và in ra màn hình kết&nbsp;quả.</p> +<p>Thay điều kiện cuối thành 101&nbsp;chân: </p> +<div class="highlight"><pre><span></span><code><span class="n">solve</span><span class="p">(</span><span class="n">ga</span> <span class="o">+</span> <span class="n">cho</span> <span class="o">==</span> <span class="mi">36</span><span class="p">,</span> <span class="n">ga</span> <span class="o">*</span> <span class="mi">2</span> <span class="o">+</span> <span class="n">cho</span> <span class="o">*</span> <span class="mi">4</span> <span class="o">==</span> <span class="mi">101</span><span class="p">)</span> +</code></pre></div> + +<p>Kết quả: <code>no solution</code> do Z3 không tìm thấy nghiệm nào thỏa&nbsp;mãn.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Tìm kiếm trong dict nhanh gấp list hàng ngàn lần2021-11-06T00:00:00+07:002021-11-06T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-11-06:/dictvslist.html<p>Python rất dễ dùng, để tìm kiếm chỉ cần gõ: <code>i in X</code> +dù X là list, dict, set, str, tuple đều&nbsp;chạy.</p> +<p>Code giống nhau, nhưng cách chạy thì khác nhau. Viết <code>i in List</code> so với +<code>i in Dict</code> hoặc <code>i in Set</code> có tốc độ khác nhau …</p><p>Python rất dễ dùng, để tìm kiếm chỉ cần gõ: <code>i in X</code> +dù X là list, dict, set, str, tuple đều&nbsp;chạy.</p> +<p>Code giống nhau, nhưng cách chạy thì khác nhau. Viết <code>i in List</code> so với +<code>i in Dict</code> hoặc <code>i in Set</code> có tốc độ khác nhau rất&nbsp;nhiều.</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1624724585603-967eb2073e03?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MzYxNzA5Nzk&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p><code>i in List</code>: list là 1 array/list, để tìm 1 giá trị, Python sẽ kiểm tra lần +lượt từng giá trị từ đầu tới cuối cho đến khi tìm thấy. Tức trường hợp xấu nhất, +khi không thấy, Python phải kiểm tra hết <code>len(List)</code> phần tử. Số lần kiểm tra +tỷ lệ thuận với số phần tử, gọi là có độ phức tạp thuật toán về thời gian <code>O(n)</code>.</p> +<p>Dùng timeit để&nbsp;đo:</p> +<div class="highlight"><pre><span></span><code><span class="err">$</span> <span class="n">python3</span> <span class="o">-</span><span class="n">m</span> <span class="n">timeit</span> <span class="o">--</span><span class="n">setup</span> <span class="s1">&#39;n=10_000; L=list(range(n))&#39;</span> <span class="s1">&#39;n in L&#39;</span> +<span class="mi">5000</span> <span class="n">loops</span><span class="p">,</span> <span class="n">best</span> <span class="n">of</span> <span class="mi">5</span><span class="p">:</span> <span class="mf">55.2</span> <span class="n">usec</span> <span class="n">per</span> <span class="n">loop</span> +<span class="err">$</span> <span class="n">python3</span> <span class="o">-</span><span class="n">m</span> <span class="n">timeit</span> <span class="o">--</span><span class="n">setup</span> <span class="s1">&#39;n=100_000; L=list(range(n))&#39;</span> <span class="s1">&#39;n in L&#39;</span> +<span class="mi">500</span> <span class="n">loops</span><span class="p">,</span> <span class="n">best</span> <span class="n">of</span> <span class="mi">5</span><span class="p">:</span> <span class="mi">569</span> <span class="n">usec</span> <span class="n">per</span> <span class="n">loop</span> +</code></pre></div> + +<p>Thấy khi list có 100_000 phần tử, trường hợp xấu nhất sẽ chậm hơn 10 lần so với +list có 10_000 phần&nbsp;tử.</p> +<p>Kiểu dict được tối ưu cho việc tìm kiếm (nên đặt tên là dictionary), việc tìm +kiếm diễn ra &#8220;tức thì&#8221;, không quan tâm dict lớn đến đâu. Việc tìm kiếm với tốc +độ cố định này gọi là thuật toán có độ phức tạp hằng số (constant), hay <code>O(1)</code>:</p> +<div class="highlight"><pre><span></span><code><span class="err">$</span> <span class="n">python3</span> <span class="o">-</span><span class="n">m</span> <span class="n">timeit</span> <span class="o">--</span><span class="n">setup</span> <span class="s1">&#39;n = 10_000; D = {i: i**2 for i in range(n)}&#39;</span> <span class="s1">&#39;n in D&#39;</span> +<span class="mi">20000000</span> <span class="n">loops</span><span class="p">,</span> <span class="n">best</span> <span class="n">of</span> <span class="mi">5</span><span class="p">:</span> <span class="mf">17.7</span> <span class="n">nsec</span> <span class="n">per</span> <span class="n">loop</span> +<span class="err">$</span> <span class="n">python3</span> <span class="o">-</span><span class="n">m</span> <span class="n">timeit</span> <span class="o">--</span><span class="n">setup</span> <span class="s1">&#39;n = 100_000; D = {i: i**2 for i in range(n)}&#39;</span> <span class="s1">&#39;n in D&#39;</span> +<span class="mi">20000000</span> <span class="n">loops</span><span class="p">,</span> <span class="n">best</span> <span class="n">of</span> <span class="mi">5</span><span class="p">:</span> <span class="mf">18.3</span> <span class="n">nsec</span> <span class="n">per</span> <span class="n">loop</span> +</code></pre></div> + +<p>Trường hợp đầu tiên với 10_000, tìm trong dict nhanh hơn trong list 3000 lần, +trường hợp thứ 2 là 30_000&nbsp;lần.</p> +<p>Khi cần tìm kiếm, nhớ dùng dict (và&nbsp;set).</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Gửi 100 triệu vào ngân hàng hôm nay, bao giờ có 1 tỷ?2021-10-31T00:00:00+07:002021-10-31T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-10-31:/compound2.html<p><img alt="img" src="https://images.unsplash.com/photo-1459257831348-f0cdd359235f?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&amp;ixlib=rb-1.2.1&amp;auto=format&amp;fit=crop&amp;w=600&amp;q=80"></p> +<p>Bài <a href="https://n.pymi.vn/compound.html">trước</a> đã giới thiệu khái niệm lãi gộp (compound interest) +với lãi suất 1%. +Lãi suất ngân hàng Vietcombank tại thời điểm viết bài là 5.5%/năm, sau 13 năm +sẽ gấp đôi, sau 44 năm +ta sẽ có 1 tỷ (gấp 10), sau 100 năm sẽ có …</p><p><img alt="img" src="https://images.unsplash.com/photo-1459257831348-f0cdd359235f?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&amp;ixlib=rb-1.2.1&amp;auto=format&amp;fit=crop&amp;w=600&amp;q=80"></p> +<p>Bài <a href="https://n.pymi.vn/compound.html">trước</a> đã giới thiệu khái niệm lãi gộp (compound interest) +với lãi suất 1%. +Lãi suất ngân hàng Vietcombank tại thời điểm viết bài là 5.5%/năm, sau 13 năm +sẽ gấp đôi, sau 44 năm +ta sẽ có 1 tỷ (gấp 10), sau 100 năm sẽ có 21&nbsp;tỷ.</p> +<p><img alt="img" src="https://n.pymi.vn/images/compound2.png"></p> +<div class="highlight"><pre><span></span><code><span class="n">rate</span> <span class="o">=</span> <span class="mf">5.5</span><span class="o">/</span><span class="mi">100</span> +<span class="n">m</span> <span class="o">=</span> <span class="mi">100</span> +<span class="n">acc</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="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">m</span> <span class="o">=</span> <span class="n">m</span> <span class="o">+</span> <span class="n">m</span> <span class="o">*</span> <span class="n">rate</span> + <span class="nb">print</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">m</span><span class="p">)</span> + <span class="n">acc</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">m</span><span class="p">)</span> + +<span class="mi">1</span> <span class="mf">105.5</span> +<span class="mi">2</span> <span class="mf">111.3025</span> +<span class="mi">3</span> <span class="mf">117.4241375</span> +<span class="mi">4</span> <span class="mf">123.8824650625</span> +<span class="mi">5</span> <span class="mf">130.6960006409375</span> +<span class="mi">6</span> <span class="mf">137.88428067618906</span> +<span class="mi">7</span> <span class="mf">145.46791611337946</span> +<span class="mi">8</span> <span class="mf">153.46865149961533</span> +<span class="mi">9</span> <span class="mf">161.90942733209417</span> +<span class="mi">10</span> <span class="mf">170.81444583535935</span> +<span class="mi">11</span> <span class="mf">180.20924035630412</span> +<span class="mi">12</span> <span class="mf">190.12074857590085</span> +<span class="mi">13</span> <span class="mf">200.57738974757538</span> +<span class="mi">14</span> <span class="mf">211.60914618369202</span> +<span class="mi">15</span> <span class="mf">223.24764922379507</span> +<span class="mi">16</span> <span class="mf">235.5262699311038</span> +<span class="mi">17</span> <span class="mf">248.4802147773145</span> +<span class="mi">18</span> <span class="mf">262.1466265900668</span> +<span class="mi">19</span> <span class="mf">276.56469105252046</span> +<span class="mi">20</span> <span class="mf">291.7757490604091</span> +<span class="mi">21</span> <span class="mf">307.82341525873164</span> +<span class="mi">22</span> <span class="mf">324.7537030979619</span> +<span class="mi">23</span> <span class="mf">342.6151567683498</span> +<span class="mi">24</span> <span class="mf">361.458990390609</span> +<span class="mi">25</span> <span class="mf">381.3392348620925</span> +<span class="mi">26</span> <span class="mf">402.3128927795076</span> +<span class="mi">27</span> <span class="mf">424.44010188238053</span> +<span class="mi">28</span> <span class="mf">447.7843074859115</span> +<span class="mi">29</span> <span class="mf">472.4124443976366</span> +<span class="mi">30</span> <span class="mf">498.3951288395066</span> +<span class="mi">31</span> <span class="mf">525.8068609256794</span> +<span class="mi">32</span> <span class="mf">554.7262382765917</span> +<span class="mi">33</span> <span class="mf">585.2361813818043</span> +<span class="mi">34</span> <span class="mf">617.4241713578035</span> +<span class="mi">35</span> <span class="mf">651.3825007824827</span> +<span class="mi">36</span> <span class="mf">687.2085383255193</span> +<span class="mi">37</span> <span class="mf">725.0050079334228</span> +<span class="mi">38</span> <span class="mf">764.8802833697611</span> +<span class="mi">39</span> <span class="mf">806.9486989550979</span> +<span class="mi">40</span> <span class="mf">851.3308773976283</span> +<span class="mi">41</span> <span class="mf">898.1540756544979</span> +<span class="mi">42</span> <span class="mf">947.5525498154952</span> +<span class="mi">43</span> <span class="mf">999.6679400553475</span> +<span class="mi">44</span> <span class="mf">1054.6496767583915</span> +<span class="mi">45</span> <span class="mf">1112.655408980103</span> +<span class="mi">46</span> <span class="mf">1173.8514564740085</span> +<span class="mi">47</span> <span class="mf">1238.4132865800789</span> +<span class="mi">48</span> <span class="mf">1306.5260173419833</span> +<span class="mi">49</span> <span class="mf">1378.3849482957924</span> +<span class="mi">50</span> <span class="mf">1454.196120452061</span> +<span class="mi">51</span> <span class="mf">1534.1769070769244</span> +<span class="mi">52</span> <span class="mf">1618.5566369661553</span> +<span class="mi">53</span> <span class="mf">1707.5772519992938</span> +<span class="mi">54</span> <span class="mf">1801.494000859255</span> +<span class="mi">55</span> <span class="mf">1900.576170906514</span> +<span class="mi">56</span> <span class="mf">2005.1078603063725</span> +<span class="mi">57</span> <span class="mf">2115.388792623223</span> +<span class="mi">58</span> <span class="mf">2231.7351762175</span> +<span class="mi">59</span> <span class="mf">2354.4806109094625</span> +<span class="mi">60</span> <span class="mf">2483.9770445094828</span> +<span class="mi">61</span> <span class="mf">2620.595781957504</span> +<span class="mi">62</span> <span class="mf">2764.728549965167</span> +<span class="mi">63</span> <span class="mf">2916.7886202132513</span> +<span class="mi">64</span> <span class="mf">3077.21199432498</span> +<span class="mi">65</span> <span class="mf">3246.458654012854</span> +<span class="mi">66</span> <span class="mf">3425.013879983561</span> +<span class="mi">67</span> <span class="mf">3613.3896433826567</span> +<span class="mi">68</span> <span class="mf">3812.126073768703</span> +<span class="mi">69</span> <span class="mf">4021.7930078259815</span> +<span class="mi">70</span> <span class="mf">4242.99162325641</span> +<span class="mi">71</span> <span class="mf">4476.356162535512</span> +<span class="mi">72</span> <span class="mf">4722.555751474965</span> +<span class="mi">73</span> <span class="mf">4982.296317806088</span> +<span class="mi">74</span> <span class="mf">5256.322615285423</span> +<span class="mi">75</span> <span class="mf">5545.420359126121</span> +<span class="mi">76</span> <span class="mf">5850.418478878058</span> +<span class="mi">77</span> <span class="mf">6172.191495216351</span> +<span class="mi">78</span> <span class="mf">6511.66202745325</span> +<span class="mi">79</span> <span class="mf">6869.803438963178</span> +<span class="mi">80</span> <span class="mf">7247.642628106153</span> +<span class="mi">81</span> <span class="mf">7646.262972651992</span> +<span class="mi">82</span> <span class="mf">8066.807436147851</span> +<span class="mi">83</span> <span class="mf">8510.481845135982</span> +<span class="mi">84</span> <span class="mf">8978.558346618462</span> +<span class="mi">85</span> <span class="mf">9472.379055682477</span> +<span class="mi">86</span> <span class="mf">9993.359903745013</span> +<span class="mi">87</span> <span class="mf">10542.994698450988</span> +<span class="mi">88</span> <span class="mf">11122.859406865793</span> +<span class="mi">89</span> <span class="mf">11734.616674243412</span> +<span class="mi">90</span> <span class="mf">12380.0205913268</span> +<span class="mi">91</span> <span class="mf">13060.921723849773</span> +<span class="mi">92</span> <span class="mf">13779.27241866151</span> +<span class="mi">93</span> <span class="mf">14537.132401687893</span> +<span class="mi">94</span> <span class="mf">15336.674683780728</span> +<span class="mi">95</span> <span class="mf">16180.191791388668</span> +<span class="mi">96</span> <span class="mf">17070.102339915044</span> +<span class="mi">97</span> <span class="mf">18008.95796861037</span> +<span class="mi">98</span> <span class="mf">18999.450656883942</span> +<span class="mi">99</span> <span class="mf">20044.42044301256</span> +<span class="mi">100</span> <span class="mf">21146.863567378252</span> +</code></pre></div> + +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Compound Interest2021-10-30T00:00:00+07:002021-10-30T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-10-30:/compound.html<blockquote> +<p>1% lãi mỗi năm, thì bao giờ tiền gửi sẽ gấp&nbsp;đôi?</p> +</blockquote> +<p>Câu trả lời sai dễ dàng là 100 năm, vì mỗi năm là 1%, 100 * 1 ==&nbsp;100.</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1579621970795-87facc2f976d?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&amp;ixlib=rb-1.2.1&amp;auto=format&amp;fit=crop&amp;w=600&amp;q=80"></p> +<p>Điều kỳ diệu ở đây là phần lãi 1% này được gộp vào để tính lãi sau năm đầu tiên. +Năm …</p><blockquote> +<p>1% lãi mỗi năm, thì bao giờ tiền gửi sẽ gấp&nbsp;đôi?</p> +</blockquote> +<p>Câu trả lời sai dễ dàng là 100 năm, vì mỗi năm là 1%, 100 * 1 ==&nbsp;100.</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1579621970795-87facc2f976d?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&amp;ixlib=rb-1.2.1&amp;auto=format&amp;fit=crop&amp;w=600&amp;q=80"></p> +<p>Điều kỳ diệu ở đây là phần lãi 1% này được gộp vào để tính lãi sau năm đầu tiên. +Năm thứ 2 lãi là 1% * (100+1), con số rất nhỏ này tăng lên rất nhanh theo thời&nbsp;gian.</p> +<p>Khái niệm này phổ biến với tên &#8220;compound interest&#8221; (lãi kép/ lãi gộp) +trong tài chính, khiến +người giàu càng giàu hơn, người nợ ngân hàng thì ngày càng nợ nhiều hơn (hello thẻ tín&nbsp;dụng).</p> +<p>Một vòng for đơn giản đủ để thấy sự kỳ diệu này, trước hết là vài con&nbsp;số:</p> +<ul> +<li>tiền gửi sẽ gấp đôi sau 70&nbsp;năm</li> +<li>sau 100 năm, tiền gửi đã là 270%, tức gấp 2.7&nbsp;lần.</li> +</ul> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="n">m</span> <span class="o">=</span> <span class="mi">100</span> +<span class="o">&gt;&gt;&gt;</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="mi">101</span><span class="p">):</span> +<span class="o">...</span> <span class="n">m</span> <span class="o">=</span> <span class="n">m</span> <span class="o">*</span> <span class="mi">1</span><span class="o">/</span><span class="mi">100</span> <span class="o">+</span> <span class="n">m</span> +<span class="o">...</span> <span class="nb">print</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">m</span><span class="p">)</span> +<span class="o">...</span> +<span class="mi">1</span> <span class="mf">101.0</span> +<span class="mi">2</span> <span class="mf">102.01</span> +<span class="mi">3</span> <span class="mf">103.0301</span> +<span class="mi">4</span> <span class="mf">104.060401</span> +<span class="mi">5</span> <span class="mf">105.10100501</span> +<span class="mi">6</span> <span class="mf">106.1520150601</span> +<span class="mi">7</span> <span class="mf">107.213535210701</span> +<span class="mi">8</span> <span class="mf">108.28567056280801</span> +<span class="mi">9</span> <span class="mf">109.36852726843608</span> +<span class="mi">10</span> <span class="mf">110.46221254112044</span> +<span class="mi">11</span> <span class="mf">111.56683466653165</span> +<span class="mi">12</span> <span class="mf">112.68250301319696</span> +<span class="mi">13</span> <span class="mf">113.80932804332893</span> +<span class="mi">14</span> <span class="mf">114.94742132376223</span> +<span class="mi">15</span> <span class="mf">116.09689553699985</span> +<span class="mi">16</span> <span class="mf">117.25786449236985</span> +<span class="mi">17</span> <span class="mf">118.43044313729355</span> +<span class="mi">18</span> <span class="mf">119.61474756866649</span> +<span class="mi">19</span> <span class="mf">120.81089504435315</span> +<span class="mi">20</span> <span class="mf">122.01900399479668</span> +<span class="mi">21</span> <span class="mf">123.23919403474464</span> +<span class="mi">22</span> <span class="mf">124.47158597509208</span> +<span class="mi">23</span> <span class="mf">125.71630183484301</span> +<span class="mi">24</span> <span class="mf">126.97346485319144</span> +<span class="mi">25</span> <span class="mf">128.24319950172335</span> +<span class="mi">26</span> <span class="mf">129.52563149674057</span> +<span class="mi">27</span> <span class="mf">130.820887811708</span> +<span class="mi">28</span> <span class="mf">132.12909668982508</span> +<span class="mi">29</span> <span class="mf">133.45038765672334</span> +<span class="mi">30</span> <span class="mf">134.78489153329056</span> +<span class="mi">31</span> <span class="mf">136.13274044862348</span> +<span class="mi">32</span> <span class="mf">137.49406785310973</span> +<span class="mi">33</span> <span class="mf">138.86900853164082</span> +<span class="mi">34</span> <span class="mf">140.2576986169572</span> +<span class="mi">35</span> <span class="mf">141.6602756031268</span> +<span class="mi">36</span> <span class="mf">143.07687835915806</span> +<span class="mi">37</span> <span class="mf">144.50764714274965</span> +<span class="mi">38</span> <span class="mf">145.95272361417713</span> +<span class="mi">39</span> <span class="mf">147.4122508503189</span> +<span class="mi">40</span> <span class="mf">148.8863733588221</span> +<span class="mi">41</span> <span class="mf">150.37523709241034</span> +<span class="mi">42</span> <span class="mf">151.87898946333445</span> +<span class="mi">43</span> <span class="mf">153.3977793579678</span> +<span class="mi">44</span> <span class="mf">154.9317571515475</span> +<span class="mi">45</span> <span class="mf">156.48107472306296</span> +<span class="mi">46</span> <span class="mf">158.0458854702936</span> +<span class="mi">47</span> <span class="mf">159.62634432499652</span> +<span class="mi">48</span> <span class="mf">161.2226077682465</span> +<span class="mi">49</span> <span class="mf">162.83483384592896</span> +<span class="mi">50</span> <span class="mf">164.46318218438824</span> +<span class="mi">51</span> <span class="mf">166.10781400623213</span> +<span class="mi">52</span> <span class="mf">167.76889214629446</span> +<span class="mi">53</span> <span class="mf">169.4465810677574</span> +<span class="mi">54</span> <span class="mf">171.14104687843496</span> +<span class="mi">55</span> <span class="mf">172.8524573472193</span> +<span class="mi">56</span> <span class="mf">174.5809819206915</span> +<span class="mi">57</span> <span class="mf">176.32679173989843</span> +<span class="mi">58</span> <span class="mf">178.09005965729742</span> +<span class="mi">59</span> <span class="mf">179.87096025387038</span> +<span class="mi">60</span> <span class="mf">181.6696698564091</span> +<span class="mi">61</span> <span class="mf">183.4863665549732</span> +<span class="mi">62</span> <span class="mf">185.32123022052292</span> +<span class="mi">63</span> <span class="mf">187.17444252272816</span> +<span class="mi">64</span> <span class="mf">189.04618694795545</span> +<span class="mi">65</span> <span class="mf">190.936648817435</span> +<span class="mi">66</span> <span class="mf">192.84601530560937</span> +<span class="mi">67</span> <span class="mf">194.77447545866548</span> +<span class="mi">68</span> <span class="mf">196.72222021325214</span> +<span class="mi">69</span> <span class="mf">198.68944241538466</span> +<span class="mi">70</span> <span class="mf">200.67633683953852</span> +<span class="mi">71</span> <span class="mf">202.6831002079339</span> +<span class="mi">72</span> <span class="mf">204.70993121001325</span> +<span class="mi">73</span> <span class="mf">206.75703052211338</span> +<span class="mi">74</span> <span class="mf">208.8246008273345</span> +<span class="mi">75</span> <span class="mf">210.91284683560787</span> +<span class="mi">76</span> <span class="mf">213.02197530396396</span> +<span class="mi">77</span> <span class="mf">215.15219505700358</span> +<span class="mi">78</span> <span class="mf">217.30371700757362</span> +<span class="mi">79</span> <span class="mf">219.47675417764935</span> +<span class="mi">80</span> <span class="mf">221.67152171942584</span> +<span class="mi">81</span> <span class="mf">223.8882369366201</span> +<span class="mi">82</span> <span class="mf">226.1271193059863</span> +<span class="mi">83</span> <span class="mf">228.38839049904615</span> +<span class="mi">84</span> <span class="mf">230.6722744040366</span> +<span class="mi">85</span> <span class="mf">232.97899714807699</span> +<span class="mi">86</span> <span class="mf">235.30878711955776</span> +<span class="mi">87</span> <span class="mf">237.66187499075335</span> +<span class="mi">88</span> <span class="mf">240.03849374066087</span> +<span class="mi">89</span> <span class="mf">242.43887867806748</span> +<span class="mi">90</span> <span class="mf">244.86326746484815</span> +<span class="mi">91</span> <span class="mf">247.31190013949663</span> +<span class="mi">92</span> <span class="mf">249.7850191408916</span> +<span class="mi">93</span> <span class="mf">252.28286933230052</span> +<span class="mi">94</span> <span class="mf">254.8056980256235</span> +<span class="mi">95</span> <span class="mf">257.35375500587975</span> +<span class="mi">96</span> <span class="mf">259.92729255593855</span> +<span class="mi">97</span> <span class="mf">262.5265654814979</span> +<span class="mi">98</span> <span class="mf">265.1518311363129</span> +<span class="mi">99</span> <span class="mf">267.80334944767606</span> +<span class="mi">100</span> <span class="mf">270.48138294215283</span> +</code></pre></div> + +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Lớp học Python PyMi Hà Nội + tp Hồ Chí Minh khai giảng 21/10/20212021-10-15T00:00:00+07:002021-10-15T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-10-15:/pymihn2110.html<p>Học viên không ở <span class="caps">HN</span> sẽ học qua livestream youtube, chat qua kênh Slack chung +với lớp Hà Nội&nbsp;offline.</p> +<p><img alt="img" src="https://n.pymi.vn/images/hn2110.jpg"></p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Python lần đầu tiên đứng số 1 trên bảng xếp hạng tiobe2021-10-15T00:00:00+07:002021-10-15T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-10-15:/tiobeno1.html<p>Trích lời <span class="caps">CEO</span> của <span class="caps">TIOBE</span>: sau 20 năm bảng xếp hạng ngôn ngữ lập trình phổ biến +<span class="caps">TIOBE</span>, Python lần đầu đứng số 1, chiếm vị trí của C, Java vào tháng&nbsp;10/2021</p> +<blockquote> +<p>For the first time in more than 20 years we have a new leader of the …</p></blockquote><p>Trích lời <span class="caps">CEO</span> của <span class="caps">TIOBE</span>: sau 20 năm bảng xếp hạng ngôn ngữ lập trình phổ biến +<span class="caps">TIOBE</span>, Python lần đầu đứng số 1, chiếm vị trí của C, Java vào tháng&nbsp;10/2021</p> +<blockquote> +<p>For the first time in more than 20 years we have a new leader of the pack: the Python programming language. The long-standing hegemony of Java and C is over. Python, which started as a simple scripting language, as an alternative to Perl, has become mature. Its ease of learning, its huge amount of libraries, and its widespread use in all kinds of domains, has made it the most popular programming language of today. Congratulations Guido van Rossum!&nbsp;Proficiat</p> +</blockquote> +<p><img alt="img" src="https://images.unsplash.com/photo-1591708441374-07a209f5cac8?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MzQyNjI3MDY&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Tham khảo:&nbsp;https://www.tiobe.com/tiobe-index/</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Build Python từ source với zlib và ssl trên Ubuntu 20.042021-10-05T00:00:00+07:002021-10-05T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-10-05:/zlib.html<p>Tiếp bài trước, sau khi compile CPython thành công, ta import thử vài thư&nbsp;viện</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">gzip</span> +<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span> + <span class="n">File</span> <span class="s2">&quot;&lt;stdin&gt;&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span> + <span class="n">File</span> <span class="s2">&quot;/root/Python-3.9.7/Lib/gzip.py&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">9</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span> + <span class="kn">import</span> <span class="nn">zlib</span> +<span class="ne">ModuleNotFoundError</span><span class="p">:</span> <span class="n">No</span> <span class="n">module</span> <span class="n">named</span> <span class="s1">&#39;zlib&#39;</span> +<span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">ssl</span> +<span class="n">Traceback …</span></code></pre></div><p>Tiếp bài trước, sau khi compile CPython thành công, ta import thử vài thư&nbsp;viện</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">gzip</span> +<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span> + <span class="n">File</span> <span class="s2">&quot;&lt;stdin&gt;&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span> + <span class="n">File</span> <span class="s2">&quot;/root/Python-3.9.7/Lib/gzip.py&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">9</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span> + <span class="kn">import</span> <span class="nn">zlib</span> +<span class="ne">ModuleNotFoundError</span><span class="p">:</span> <span class="n">No</span> <span class="n">module</span> <span class="n">named</span> <span class="s1">&#39;zlib&#39;</span> +<span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">ssl</span> +<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span> + <span class="n">File</span> <span class="s2">&quot;&lt;stdin&gt;&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span> + <span class="n">File</span> <span class="s2">&quot;/root/Python-3.9.7/Lib/ssl.py&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">98</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span> + <span class="kn">import</span> <span class="nn">_ssl</span> <span class="c1"># if we can&#39;t import it, let the error propagate</span> +<span class="ne">ModuleNotFoundError</span><span class="p">:</span> <span class="n">No</span> <span class="n">module</span> <span class="n">named</span> <span class="s1">&#39;_ssl&#39;</span> +</code></pre></div> + +<p><img alt="img" src="https://images.unsplash.com/photo-1626862647712-a38156ab0488?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MzMzOTg1ODA&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Bản Python này bị thiếu các thư viện C cần thiết nên khi compile, kết quả không +có các thư viện tương ứng,&nbsp;cài:</p> +<div class="highlight"><pre><span></span><code>sudo apt-get install -y zlib1g-dev libssl-dev +</code></pre></div> + +<p>Sau khi cài xong, chạy lại <code>./configure &amp;&amp; make</code></p> +<p>Cuối output của make có hiện ra&nbsp;đoạn: </p> +<div class="highlight"><pre><span></span><code>Python build finished successfully! +The necessary bits to build these optional modules were not found: +_bz2 _curses _curses_panel +_dbm _gdbm _lzma +_sqlite3 _tkinter _uuid +readline +</code></pre></div> + +<p>cho thấy bản build này còn thiếu những thư viện nào, như: sqlite3, tkinter,&nbsp;bz2.</p> +<p>Nhưng giờ thì đã có zlib và&nbsp;ssl</p> +<div class="highlight"><pre><span></span><code><span class="o">./</span><span class="n">python</span> +<span class="n">Python</span> <span class="mf">3.9.7</span> <span class="p">(</span><span class="n">default</span><span class="p">,</span> <span class="n">Oct</span> <span class="mi">5</span> <span class="mi">2021</span><span class="p">,</span> <span class="mi">01</span><span class="p">:</span><span class="mi">53</span><span class="p">:</span><span class="mi">51</span><span class="p">)</span> +<span class="p">[</span><span class="n">GCC</span> <span class="mf">9.3.0</span><span class="p">]</span> <span class="n">on</span> <span class="n">linux</span> +<span class="n">Type</span> <span class="s2">&quot;help&quot;</span><span class="p">,</span> <span class="s2">&quot;copyright&quot;</span><span class="p">,</span> <span class="s2">&quot;credits&quot;</span> <span class="ow">or</span> <span class="s2">&quot;license&quot;</span> <span class="k">for</span> <span class="n">more</span> <span class="n">information</span><span class="o">.</span> +<span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">gzip</span> +<span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">ssl</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">ssl</span> +<span class="o">&lt;</span><span class="n">module</span> <span class="s1">&#39;ssl&#39;</span> <span class="kn">from</span> <span class="s1">&#39;/root/Python-3.9.7/Lib/ssl.py&#39;</span><span class="o">&gt;</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">gzip</span> +<span class="o">&lt;</span><span class="n">module</span> <span class="s1">&#39;gzip&#39;</span> <span class="kn">from</span> <span class="s1">&#39;/root/Python-3.9.7/Lib/gzip.py&#39;</span><span class="o">&gt;</span> +<span class="o">&gt;&gt;&gt;</span> <span class="kn">import</span> <span class="nn">zlib</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">zlib</span> +<span class="o">&lt;</span><span class="n">module</span> <span class="s1">&#39;zlib&#39;</span> <span class="kn">from</span> <span class="s1">&#39;/root/Python-3.9.7/build/lib.linux-x86_64-3.9/zlib.cpython-39-x86_64-linux-gnu.so&#39;</span><span class="o">&gt;</span> +</code></pre></div> + +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Build Python từ source trên Ubuntu 20.042021-10-03T00:00:00+07:002021-10-03T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-10-03:/build.html<p>Build phần mềm từ source dù là chuyện phổ biến trong giới mã nguồn mở từ xưa nhưng dần dần trở thành bí kíp thất +truyền với việc các package manager (như apt, yum, &#8230;) đều cài sẵn binary (sản phẩm của việc&nbsp;build).</p> +<p>Build từ source không tiện lợi cho …</p><p>Build phần mềm từ source dù là chuyện phổ biến trong giới mã nguồn mở từ xưa nhưng dần dần trở thành bí kíp thất +truyền với việc các package manager (như apt, yum, &#8230;) đều cài sẵn binary (sản phẩm của việc&nbsp;build).</p> +<p>Build từ source không tiện lợi cho mục đích cài phần mềm hàng ngày, nhưng là một kiến thức tốt trong học tập và nghiên&nbsp;cứu.</p> +<p>Build CPython bản mới nhất trên Ubuntu cũng không có khó khăn gì, mất khoảng 3-5 phút tùy tốc độ máy&nbsp;tính.</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1570760295437-3627311f8fbe?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MzMyNzgzOTE&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<h3>Chuẩn&nbsp;bị</h3> +<p>Để thực hiện bài này, cần có 4 phần&nbsp;mềm:</p> +<ul> +<li>curl để tải file source của&nbsp;Python</li> +<li>tar để giải&nbsp;nén</li> +<li>gcc có C compiler - để&nbsp;build</li> +<li>make để chạy lệnh&nbsp;build</li> +</ul> +<p>Chạy trên Ubuntu 20.04.3 <span class="caps">LTS</span></p> +<p>Cài:</p> +<div class="highlight"><pre><span></span><code>sudo apt update &amp;&amp; sudo apt-get install -y curl make gcc tar +</code></pre></div> + +<h3>Bắt&nbsp;đầu</h3> +<p>Tải từ trang chủ&nbsp;https://www.python.org/downloads/release/python-397/</p> +<div class="highlight"><pre><span></span><code>curl -LO https://www.python.org/ftp/python/3.9.7/Python-3.9.7.tgz +</code></pre></div> + +<p>Giải&nbsp;nén</p> +<div class="highlight"><pre><span></span><code>tar xf Python-3.9.7.tgz; cd Python-3.9.7 +</code></pre></div> + +<p>Build - theo hướng dẫn trong file <span class="caps">README</span>.rst</p> +<div class="highlight"><pre><span></span><code> ./configure + make + # make test + # sudo make install +</code></pre></div> + +<p><code>configure</code> là 1 shell script, chạy các câu lệnh kiểm tra các điều kiện cần thiết (như có C compiler chưa, &#8230;) và sinh ra file&nbsp;Makefile.</p> +<p><code>make</code> chạy lệnh trong Makefile, nếu quá trình thành công sẽ tạo ra file <code>python</code>.</p> +<p>Đây chính là chương trình <code>python</code> thu&nbsp;được.</p> +<div class="highlight"><pre><span></span><code># ./python +Python 3.9.7 (default, Oct 3 2021, 16:21:43) +[GCC 9.3.0] on linux +</code></pre></div> + +<p>Sau khi xong có thể chạy thêm <code>make test</code> để đảm bảo <code>python</code> chạy thành công các test, và <code>sudo make install</code> để cài vào máy thay Python trên&nbsp;máy.</p> +<p>Trong quá trình compile, người dùng sẽ nhìn thấy các thành phần của Python nằm ở file C code nào - không nhất thiết phải biết code C, nhưng ít ra biết nó ở đâu. Ví&nbsp;dụ:</p> +<div class="highlight"><pre><span></span><code><span class="nv">gcc</span><span class="w"> </span><span class="o">-</span><span class="nv">pthread</span><span class="w"> </span><span class="o">-</span><span class="nv">c</span><span class="w"> </span><span class="o">-</span><span class="nv">Wno</span><span class="o">-</span><span class="nv">unused</span><span class="o">-</span><span class="nb">result</span><span class="w"> </span><span class="o">-</span><span class="nv">Wsign</span><span class="o">-</span><span class="nv">compare</span><span class="w"> </span><span class="o">-</span><span class="nv">DNDEBUG</span><span class="w"> </span><span class="o">-</span><span class="nv">g</span><span class="w"> </span><span class="o">-</span><span class="nv">fwrapv</span><span class="w"> </span><span class="o">-</span><span class="nv">O3</span><span class="w"> </span><span class="o">-</span><span class="nv">Wall</span><span class="w"> </span><span class="o">-</span><span class="nv">std</span><span class="o">=</span><span class="nv">c99</span><span class="w"> </span><span class="o">-</span><span class="nv">Wextra</span><span class="w"> </span><span class="o">-</span><span class="nv">Wno</span><span class="o">-</span><span class="nv">unused</span><span class="o">-</span><span class="nb">result</span><span class="w"> </span><span class="o">-</span><span class="nv">Wno</span><span class="o">-</span><span class="nv">unused</span><span class="o">-</span><span class="nv">parameter</span><span class="w"> </span><span class="o">-</span><span class="nv">Wno</span><span class="o">-</span><span class="nv">missing</span><span class="o">-</span><span class="nv">field</span><span class="o">-</span><span class="nv">initializers</span><span class="w"> </span><span class="o">-</span><span class="nv">Werror</span><span class="o">=</span><span class="nv">implicit</span><span class="o">-</span><span class="nv">function</span><span class="o">-</span><span class="nv">declaration</span><span class="w"> </span><span class="o">-</span><span class="nv">fvisibility</span><span class="o">=</span><span class="nv">hidden</span><span class="w"> </span><span class="o">-</span><span class="nv">I</span>.<span class="o">/</span><span class="k">Include</span><span class="o">/</span><span class="nv">internal</span><span class="w"> </span><span class="o">-</span><span class="nv">I</span>.<span class="w"> </span><span class="o">-</span><span class="nv">I</span>.<span class="o">/</span><span class="k">Include</span><span class="w"> </span><span class="o">-</span><span class="nv">DPy_BUILD_CORE</span><span class="w"> </span><span class="o">-</span><span class="nv">o</span><span class="w"> </span><span class="nv">Objects</span><span class="o">/</span><span class="nv">listobject</span>.<span class="nv">o</span><span class="w"> </span><span class="nv">Objects</span><span class="o">/</span><span class="nv">listobject</span>.<span class="nv">c</span><span class="w"></span> +</code></pre></div> + +<p>Hết.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>PySpark đi phỏng vấn đếm từ count words2021-09-19T00:00:00+07:002021-09-19T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-09-19:/sparkwc.html<p><span class="dquo">&#8220;</span>count words&#8221; là 1 bài phỏng vấn kinh điển trong ngành <span class="caps">IT</span>, nó không quá khó/thuật toán/thách đố, mà lại rất thực tế, yêu cầu đủ các kiến thức cần có để viết&nbsp;code:</p> +<ul> +<li>dùng&nbsp;dictionary</li> +<li>viết vòng lặp&nbsp;for</li> +<li>có thể cần viết&nbsp;if</li> +<li>xử lý&nbsp;string …</li></ul><p><span class="dquo">&#8220;</span>count words&#8221; là 1 bài phỏng vấn kinh điển trong ngành <span class="caps">IT</span>, nó không quá khó/thuật toán/thách đố, mà lại rất thực tế, yêu cầu đủ các kiến thức cần có để viết&nbsp;code:</p> +<ul> +<li>dùng&nbsp;dictionary</li> +<li>viết vòng lặp&nbsp;for</li> +<li>có thể cần viết&nbsp;if</li> +<li>xử lý&nbsp;string</li> +<li>sort (sắp xếp) kết quả bằng dict&nbsp;value</li> +<li>đọc file (<span class="caps">IO</span>)</li> +</ul> +<p><img alt="img" src="https://images.unsplash.com/photo-1598210854169-af04499e4899?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MzIwMjY5Mzk&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Nó không quá dễ/beginner như <a href="https://en.wikipedia.org/wiki/Fizz_buzz">FizzBuzz</a>, hoàn toàn có thể làm bài test &#8220;số 2&#8221; sau khi ứng viên giải bài FizzBuzz sau 5 phút, cũng không quá khó/thuật toán kiểu &#8220;leetcode.com&#8221;, nên có thể dành test cho cả non-developer (như Sysadmin/devops/<span class="caps">QA</span>&#8230;) lẫn developer (<span class="caps">PS</span>: tác giả bài viết trong link cuối bài là người phỏng vấn các kỹ sư tại Canonical - công ty đứng sau Ubuntu). Python giải bài này dùng dict rất đơn giản, thậm chí cực đơn giản khi có sẵn kiểu&nbsp;Counter:</p> +<div class="highlight"><pre><span></span><code><span class="n">In</span> <span class="p">[</span><span class="mi">42</span><span class="p">]:</span> <span class="kn">from</span> <span class="nn">collections</span> <span class="kn">import</span> <span class="n">Counter</span> + +<span class="n">In</span> <span class="p">[</span><span class="mi">56</span><span class="p">]:</span> <span class="n">t</span> <span class="o">=</span> <span class="s2">&quot;ga meo Bo meo bo meo&quot;</span> + +<span class="n">In</span> <span class="p">[</span><span class="mi">57</span><span class="p">]:</span> <span class="n">Counter</span><span class="p">(</span><span class="n">t</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span><span class="o">.</span><span class="n">split</span><span class="p">())</span><span class="o">.</span><span class="n">most_common</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> +<span class="n">Out</span><span class="p">[</span><span class="mi">57</span><span class="p">]:</span> <span class="p">[(</span><span class="s1">&#39;meo&#39;</span><span class="p">,</span> <span class="mi">3</span><span class="p">),</span> <span class="p">(</span><span class="s1">&#39;bo&#39;</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span> <span class="p">(</span><span class="s1">&#39;ga&#39;</span><span class="p">,</span> <span class="mi">1</span><span class="p">)]</span> +</code></pre></div> + +<p>dùng sẵn chuẩn Pythonic vậy nên khi đi phỏng vấn, nhiều khi bị coi là &#8220;hack&#8221;/&#8221;cheat&#8221;, bắt phải tự viết bằng&nbsp;dict:</p> +<div class="highlight"><pre><span></span><code><span class="n">In</span> <span class="p">[</span><span class="mi">47</span><span class="p">]:</span> <span class="n">t</span> <span class="o">=</span> <span class="s2">&quot;ga meo Bo meo bo meo&quot;</span> + +<span class="n">In</span> <span class="p">[</span><span class="mi">48</span><span class="p">]:</span> <span class="n">d</span> <span class="o">=</span> <span class="p">{}</span> + +<span class="n">In</span> <span class="p">[</span><span class="mi">49</span><span class="p">]:</span> <span class="k">for</span> <span class="n">word</span> <span class="ow">in</span> <span class="n">t</span><span class="o">.</span><span class="n">lower</span><span class="p">()</span><span class="o">.</span><span class="n">split</span><span class="p">():</span> + <span class="o">...</span><span class="p">:</span> <span class="n">d</span><span class="p">[</span><span class="n">word</span><span class="p">]</span> <span class="o">=</span> <span class="n">d</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">word</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="o">...</span><span class="p">:</span> + +<span class="n">In</span> <span class="p">[</span><span class="mi">55</span><span class="p">]:</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">d</span><span class="o">.</span><span class="n">items</span><span class="p">(),</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">e</span><span class="p">:</span> <span class="n">e</span><span class="p">[</span><span class="mi">1</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="mi">10</span><span class="p">]</span> +<span class="n">Out</span><span class="p">[</span><span class="mi">55</span><span class="p">]:</span> <span class="p">[(</span><span class="s1">&#39;meo&#39;</span><span class="p">,</span> <span class="mi">3</span><span class="p">),</span> <span class="p">(</span><span class="s1">&#39;bo&#39;</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span> <span class="p">(</span><span class="s1">&#39;ga&#39;</span><span class="p">,</span> <span class="mi">1</span><span class="p">)]</span> +</code></pre></div> + +<p>Giải bằng PySpark thì&nbsp;sao?</p> +<p>import và tạo kết nối đến Spark rồi đọc file text&nbsp;vào:</p> +<div class="highlight"><pre><span></span><code><span class="n">In</span> <span class="p">[</span><span class="mi">1</span><span class="p">]:</span> <span class="kn">from</span> <span class="nn">pyspark.sql</span> <span class="kn">import</span> <span class="n">SparkSession</span> + +<span class="n">In</span> <span class="p">[</span><span class="mi">2</span><span class="p">]:</span> <span class="n">spark</span> <span class="o">=</span> <span class="n">SparkSession</span><span class="o">.</span><span class="n">builder</span><span class="o">.</span><span class="n">getOrCreate</span><span class="p">()</span> +<span class="o">...</span> +<span class="n">To</span> <span class="n">adjust</span> <span class="n">logging</span> <span class="n">level</span> <span class="n">use</span> <span class="n">sc</span><span class="o">.</span><span class="n">setLogLevel</span><span class="p">(</span><span class="n">newLevel</span><span class="p">)</span><span class="o">.</span> <span class="n">For</span> <span class="n">SparkR</span><span class="p">,</span> <span class="n">use</span> <span class="nb">set</span> + +<span class="n">In</span> <span class="p">[</span><span class="mi">3</span><span class="p">]:</span> <span class="n">text</span> <span class="o">=</span> <span class="n">spark</span><span class="o">.</span><span class="n">read</span><span class="o">.</span><span class="n">text</span><span class="p">(</span><span class="s2">&quot;kjvbible.txt&quot;</span><span class="p">)</span> +</code></pre></div> + +<p>File kjvbible.text nặng 4.<span class="caps">2MB</span> tải từ&nbsp;https://github.com/benhoyt/countwords/raw/master/kjvbible.txt</p> +<p>Mỗi dòng sẽ được cho vào thành 1 row trong cột&nbsp;&#8220;value&#8221;:</p> +<div class="highlight"><pre><span></span><code><span class="n">In</span> <span class="p">[</span><span class="mi">60</span><span class="p">]:</span> <span class="n">text</span><span class="o">.</span><span class="n">printSchema</span><span class="p">()</span> +<span class="n">root</span> + <span class="o">|--</span> <span class="n">value</span><span class="p">:</span> <span class="n">string</span> <span class="p">(</span><span class="n">nullable</span> <span class="o">=</span> <span class="n">true</span><span class="p">)</span> + +<span class="n">In</span> <span class="p">[</span><span class="mi">64</span><span class="p">]:</span> <span class="n">text</span><span class="o">.</span><span class="n">show</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> +<span class="o">+--------------------+</span> +<span class="o">|</span> <span class="n">value</span><span class="o">|</span> +<span class="o">+--------------------+</span> +<span class="o">|</span><span class="n">The</span> <span class="n">Old</span> <span class="n">Testament</span><span class="o">...|</span> +<span class="o">|</span> <span class="o">|</span> +<span class="o">|</span> <span class="o">|</span> +<span class="o">|</span> <span class="o">|</span> +<span class="o">|</span> <span class="o">|</span> +<span class="o">|</span><span class="n">The</span> <span class="n">First</span> <span class="n">Book</span> <span class="n">of</span><span class="o">...|</span> +<span class="o">|</span> <span class="o">|</span> +<span class="o">|</span> <span class="o">|</span> +<span class="o">|</span><span class="mi">1</span><span class="p">:</span><span class="mi">1</span> <span class="n">In</span> <span class="n">the</span> <span class="n">beginn</span><span class="o">...|</span> +<span class="o">|</span> <span class="o">|</span> +<span class="o">+--------------------+</span> +</code></pre></div> + +<p>Biến thành chữ thường (lower) rồi cắt thành các từ sử dụng split, split ở đây khác với string method split của Python, đây thực chất là split của Java string, nó nhận vào 1 regex pattern, ở đây dùng &#8220;\s+&#8221; tức 1 hay nhiều ký tự whitespace (space, tab,&nbsp;newline):</p> +<div class="highlight"><pre><span></span><code><span class="n">In</span> <span class="p">[</span><span class="mi">65</span><span class="p">]:</span> <span class="kn">from</span> <span class="nn">pyspark.sql.functions</span> <span class="kn">import</span> <span class="o">*</span> + +<span class="n">In</span> <span class="p">[</span><span class="mi">69</span><span class="p">]:</span> <span class="n">text</span><span class="o">.</span><span class="n">select</span><span class="p">(</span><span class="n">split</span><span class="p">(</span><span class="n">lower</span><span class="p">(</span><span class="n">text</span><span class="o">.</span><span class="n">value</span><span class="p">),</span> <span class="s2">&quot;\s+&quot;</span><span class="p">))</span><span class="o">.</span><span class="n">show</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> +<span class="o">+----------------------------+</span> +<span class="o">|</span><span class="n">split</span><span class="p">(</span><span class="n">lower</span><span class="p">(</span><span class="n">value</span><span class="p">),</span> \<span class="n">s</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="o">+----------------------------+</span> +<span class="o">|</span> <span class="p">[</span><span class="n">the</span><span class="p">,</span> <span class="n">old</span><span class="p">,</span> <span class="n">testam</span><span class="o">...|</span> +<span class="o">|</span> <span class="p">[]</span><span class="o">|</span> +<span class="o">|</span> <span class="p">[]</span><span class="o">|</span> +<span class="o">|</span> <span class="p">[]</span><span class="o">|</span> +<span class="o">|</span> <span class="p">[]</span><span class="o">|</span> +<span class="o">|</span> <span class="p">[</span><span class="n">the</span><span class="p">,</span> <span class="n">first</span><span class="p">,</span> <span class="n">book</span><span class="o">...|</span> +<span class="o">|</span> <span class="p">[]</span><span class="o">|</span> +<span class="o">|</span> <span class="p">[]</span><span class="o">|</span> +<span class="o">|</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">in</span><span class="p">,</span> <span class="n">the</span><span class="p">,</span> <span class="n">be</span><span class="o">...|</span> +<span class="o">|</span> <span class="p">[]</span><span class="o">|</span> +<span class="o">+----------------------------+</span> +</code></pre></div> + +<p>Thay vì mỗi string ban đầu ở mỗi dòng, giờ ta có list (spark/Java gọi là array) các string ở mỗi dòng. Cần nối các list này lại với nhau rồi biến cái list đó thành các&nbsp;dòng.</p> +<div class="highlight"><pre><span></span><code><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="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="o">=&gt;</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="o">+</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="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="o">=&gt;</span> <span class="n">các</span> <span class="n">dòng</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="mf">6.</span> +</code></pre></div> + +<p>Function <code>explode</code> thực hiện việc&nbsp;này:</p> +<div class="highlight"><pre><span></span><code><span class="n">In</span> <span class="p">[</span><span class="mi">70</span><span class="p">]:</span> <span class="n">text</span><span class="o">.</span><span class="n">select</span><span class="p">(</span><span class="n">explode</span><span class="p">(</span><span class="n">split</span><span class="p">(</span><span class="n">lower</span><span class="p">(</span><span class="n">text</span><span class="o">.</span><span class="n">value</span><span class="p">),</span> <span class="s2">&quot;\s+&quot;</span><span class="p">)))</span><span class="o">.</span><span class="n">show</span><span class="p">()</span> +<span class="o">+---------+</span> +<span class="o">|</span> <span class="n">col</span><span class="o">|</span> +<span class="o">+---------+</span> +<span class="o">|</span> <span class="n">the</span><span class="o">|</span> +<span class="o">|</span> <span class="n">old</span><span class="o">|</span> +<span class="o">|</span><span class="n">testament</span><span class="o">|</span> +<span class="o">|</span> <span class="n">of</span><span class="o">|</span> +</code></pre></div> + +<p>Giờ đặt lại tên cột cho hay với alias, bỏ đi các dòng empty, rồi nhóm (groupBy) các từ giống nhau lại, rồi đếm (count), sắp xếp theo từ nào có count nhiều nhất, giảm dần, lấy 10 từ&nbsp;top:</p> +<div class="highlight"><pre><span></span><code><span class="n">In</span> <span class="p">[</span><span class="mi">97</span><span class="p">]:</span> <span class="n">text</span><span class="o">.</span><span class="n">select</span><span class="p">(</span><span class="n">explode</span><span class="p">(</span><span class="n">split</span><span class="p">(</span><span class="n">lower</span><span class="p">(</span><span class="n">text</span><span class="o">.</span><span class="n">value</span><span class="p">),</span> <span class="s2">&quot;\s+&quot;</span><span class="p">))</span><span class="o">.</span><span class="n">alias</span><span class="p">(</span><span class="s2">&quot;word&quot;</span><span class="p">))</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="s2">&quot;word != &#39;&#39;&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">groupBy</span><span class="p">(</span><span class="s2">&quot;word&quot;</span><span class="p">)</span><span class="o">.</span><span class="n">count</span><span class="p">()</span><span class="o">.</span><span class="n">sort</span><span class="p">(</span><span class="s2">&quot;count&quot;</span><span class="p">,</span> <span class="n">ascending</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span><span class="o">.</span><span class="n">show</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> +<span class="o">+-----+-----+</span> +<span class="o">|</span> <span class="n">word</span><span class="o">|</span><span class="n">count</span><span class="o">|</span> +<span class="o">+-----+-----+</span> +<span class="o">|</span> <span class="n">the</span><span class="o">|</span><span class="mi">64015</span><span class="o">|</span> +<span class="o">|</span> <span class="ow">and</span><span class="o">|</span><span class="mi">51313</span><span class="o">|</span> +<span class="o">|</span> <span class="n">of</span><span class="o">|</span><span class="mi">34634</span><span class="o">|</span> +<span class="o">|</span> <span class="n">to</span><span class="o">|</span><span class="mi">13567</span><span class="o">|</span> +<span class="o">|</span> <span class="n">that</span><span class="o">|</span><span class="mi">12784</span><span class="o">|</span> +<span class="o">|</span> <span class="ow">in</span><span class="o">|</span><span class="mi">12503</span><span class="o">|</span> +<span class="o">|</span> <span class="n">he</span><span class="o">|</span><span class="mi">10261</span><span class="o">|</span> +<span class="o">|</span><span class="n">shall</span><span class="o">|</span> <span class="mi">9838</span><span class="o">|</span> +<span class="o">|</span> <span class="n">unto</span><span class="o">|</span> <span class="mi">8987</span><span class="o">|</span> +<span class="o">|</span> <span class="k">for</span><span class="o">|</span> <span class="mi">8810</span><span class="o">|</span> +<span class="o">+-----+-----+</span> +<span class="n">only</span> <span class="n">showing</span> <span class="n">top</span> <span class="mi">10</span> <span class="n">rows</span> +</code></pre></div> + +<p>Kết quả trùng khớp với https://github.com/benhoyt/countwords/blob/master/output.txt (x10 - do trong repo họ tạo file text 10&nbsp;lần).</p> +<p>Tham&nbsp;khảo:</p> +<ul> +<li><a href="https://benhoyt.com/writings/count-words/">https://benhoyt.com/writings/count-words/</a></li> +</ul> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>PySpark bigdata giải bài toán ProjectEuler 12021-09-18T00:00:00+07:002021-09-18T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-09-18:/sparkpe1.html<p>pandas là công cụ tuyệt vời để xử lý, khám phá dữ liệu dạng bảng như Excel, nó là phần không thể thiếu với các ngành &#8220;data analysis&#8221;, &#8220;data&nbsp;science&#8221;.</p> +<p>pandas có 1 nhược điểm/yêu cầu: là dữ liệu phải nhét vừa vào <span class="caps">RAM</span>. Tức nếu có bộ dữ …</p><p>pandas là công cụ tuyệt vời để xử lý, khám phá dữ liệu dạng bảng như Excel, nó là phần không thể thiếu với các ngành &#8220;data analysis&#8221;, &#8220;data&nbsp;science&#8221;.</p> +<p>pandas có 1 nhược điểm/yêu cầu: là dữ liệu phải nhét vừa vào <span class="caps">RAM</span>. Tức nếu có bộ dữ liệu lớn hơn <span class="caps">RAM</span> thì sẽ khó/không xử lý được/phải dùng thư viện khác (như dash,&nbsp;etc&#8230;)</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1630011725376-bd68a6403318?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MzE5NzM5OTY&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>PySpark là 1 giải pháp, Spark đã quá nổi tiếng trong lĩnh vực BigData, thì giờ đây có thể cài, chạy PySpark, gần như Pandas để tính toán dữ liệu, với ưu điểm có thể xử lý song song (nhanh hơn), xử lý được dữ liệu lớn hơn <span class="caps">RAM</span>, dùng nhiều máy tính. Người dùng pandas nên biết thêm pyspark để dùng khi cần, chứ không phải để thay thế trong mọi trường&nbsp;hợp.</p> +<h2>Cài&nbsp;đặt</h2> +<p>Pandas dùng C ở bên dưới, mà C thì không cần cài gì để chạy cả. +Spark dùng Java, để chạy phải cài Java Runtime Environment (<span class="caps">JRE</span>), trên Ubuntu 20.04&nbsp;gõ:</p> +<div class="highlight"><pre><span></span><code>sudo apt-get update &amp;&amp; sudo apt-get install -y default-jre +</code></pre></div> + +<p>Tạo 1 venv mới,&nbsp;cài </p> +<div class="highlight"><pre><span></span><code>pip install pyspark +</code></pre></div> + +<h2>Giải ProjectEuler&nbsp;1</h2> +<p>https://projecteuler.net/problem=1</p> +<blockquote> +<p>Tính tổng các số nhỏ hơn 1000 chia hết cho 3 hoặc&nbsp;5.</p> +</blockquote> +<p>Bật Python trong venv lên và&nbsp;import </p> +<div class="highlight"><pre><span></span><code><span class="n">In</span> <span class="p">[</span><span class="mi">1</span><span class="p">]:</span> <span class="kn">from</span> <span class="nn">pyspark.sql</span> <span class="kn">import</span> <span class="n">SparkSession</span> + +<span class="n">In</span> <span class="p">[</span><span class="mi">2</span><span class="p">]:</span> <span class="n">spark</span> <span class="o">=</span> <span class="n">SparkSession</span><span class="o">.</span><span class="n">builder</span><span class="o">.</span><span class="n">getOrCreate</span><span class="p">()</span> +<span class="o">...</span> +<span class="n">Using</span> <span class="n">Spark</span><span class="s1">&#39;s default log4j profile: org/apache/spark/log4j-defaults.properties</span> +<span class="n">Setting</span> <span class="n">default</span> <span class="n">log</span> <span class="n">level</span> <span class="n">to</span> <span class="s2">&quot;WARN&quot;</span><span class="o">.</span> +<span class="n">To</span> <span class="n">adjust</span> <span class="n">logging</span> <span class="n">level</span> <span class="n">use</span> <span class="n">sc</span><span class="o">.</span><span class="n">setLogLevel</span><span class="p">(</span><span class="n">newLevel</span><span class="p">)</span><span class="o">.</span> <span class="n">For</span> <span class="n">SparkR</span><span class="p">,</span> <span class="n">use</span> <span class="n">setLogLevel</span><span class="p">(</span><span class="n">newLevel</span><span class="p">)</span><span class="o">.</span> +</code></pre></div> + +<p>Tạo dataframe, đầu vào là list của các dòng, mỗi dòng là 1 tuple, do bài này chỉ dùng 1 cột nên tạo tuple 1 phần tử <code>(i,)</code>, schema là khái niệm trong <span class="caps">SQL</span> database, nói về tên cột (có thể kèm&nbsp;kiểu):</p> +<div class="highlight"><pre><span></span><code> <span class="p">[</span><span class="mi">10</span><span class="p">]:</span> <span class="n">df</span> <span class="o">=</span> <span class="n">spark</span><span class="o">.</span><span class="n">createDataFrame</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="nb">range</span><span class="p">(</span><span class="mi">1000</span><span class="p">)),</span> <span class="n">schema</span><span class="o">=</span><span class="p">[</span><span class="s1">&#39;n&#39;</span><span class="p">])</span> +</code></pre></div> + +<p>Giải:</p> +<div class="highlight"><pre><span></span><code><span class="n">In</span> <span class="p">[</span><span class="mi">12</span><span class="p">]:</span> <span class="n">df</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="s1">&#39;n % 3 == 0 or n % 5 == 0&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">agg</span><span class="p">({</span><span class="s1">&#39;n&#39;</span><span class="p">:</span> <span class="s1">&#39;sum&#39;</span><span class="p">})</span><span class="o">.</span><span class="n">show</span><span class="p">()</span> +<span class="o">+------+</span> +<span class="o">|</span><span class="nb">sum</span><span class="p">(</span><span class="n">n</span><span class="p">)</span><span class="o">|</span> +<span class="o">+------+</span> +<span class="o">|</span><span class="mi">233168</span><span class="o">|</span> +<span class="o">+------+</span> +</code></pre></div> + +<p><code>PySpark</code> không tính toán cho tới khi gọi <code>show()</code> hay <code>collect()</code></p> +<p>Bonus: giải bằng&nbsp;pandas:</p> +<div class="highlight"><pre><span></span><code><span class="n">In</span> <span class="p">[</span><span class="mi">24</span><span class="p">]:</span> <span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="nn">pd</span> + +<span class="n">In</span> <span class="p">[</span><span class="mi">25</span><span class="p">]:</span> <span class="n">pdf</span> <span class="o">=</span> <span class="n">pd</span><span class="o">.</span><span class="n">DataFrame</span><span class="p">({</span><span class="s1">&#39;n&#39;</span><span class="p">:</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1000</span><span class="p">)})</span> + +<span class="n">In</span> <span class="p">[</span><span class="mi">26</span><span class="p">]:</span> <span class="n">pdf</span><span class="o">.</span><span class="n">query</span><span class="p">(</span><span class="s1">&#39;n % 3 == 0 or n % 5 == 0&#39;</span><span class="p">)</span><span class="o">.</span><span class="n">agg</span><span class="p">({</span><span class="s1">&#39;n&#39;</span><span class="p">:</span> <span class="s1">&#39;sum&#39;</span><span class="p">})</span> +<span class="n">Out</span><span class="p">[</span><span class="mi">26</span><span class="p">]:</span> +<span class="n">n</span> <span class="mi">233168</span> +<span class="n">dtype</span><span class="p">:</span> <span class="n">int64</span> +</code></pre></div> + +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>chạy web server bằng 1 câu lệnh Python2021-09-17T00:00:00+07:002021-09-17T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-09-17:/httpd.html<p>Python có sẵn trong stdlib thư viện http để bật ngay 1 <span class="caps">HTTP</span> server/webserver, +khi chạy THẬT, bạn có thể dùng <span class="caps">NGINX</span>, nhưng đôi khi cần &#8220;test nhanh&#8221;, cài và +config <span class="caps">NGINX</span> là một chuyện không hề&nbsp;nhanh.</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1578951141665-41b333cb63cc?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MzE4ODY3MDk&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Gõ:</p> +<div class="highlight"><pre><span></span><code>$ python3 -m http.server +Serving HTTP on <span class="m">0</span>.0.0 …</code></pre></div><p>Python có sẵn trong stdlib thư viện http để bật ngay 1 <span class="caps">HTTP</span> server/webserver, +khi chạy THẬT, bạn có thể dùng <span class="caps">NGINX</span>, nhưng đôi khi cần &#8220;test nhanh&#8221;, cài và +config <span class="caps">NGINX</span> là một chuyện không hề&nbsp;nhanh.</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1578951141665-41b333cb63cc?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MzE4ODY3MDk&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Gõ:</p> +<div class="highlight"><pre><span></span><code>$ python3 -m http.server +Serving HTTP on <span class="m">0</span>.0.0.0 port <span class="m">8000</span> <span class="o">(</span>http://0.0.0.0:8000/<span class="o">)</span> ... +</code></pre></div> + +<p>Truy cập qua trình duyệt gõ:&nbsp;localhost:8000</p> +<p>thậm chí máy khác cùng mạng cũng có thể truy cập. Server này trả về list các +file trong thư mục hiện tại. Có thể tạo 1 file index.html để trả về nội dung +file này khi người dùng truy&nbsp;cập.</p> +<p>Đó là sức mạnh thư viện có sẵn của Python, rất tiện và có ở đâu có Python3. +Một nhược điểm là nó có thể hơi chậm so với mong muốn, dùng busybox cũng chạy +được 1 câu&nbsp;lệnh:</p> +<div class="highlight"><pre><span></span><code>$ busybox httpd -fp8000 +</code></pre></div> + +<p>busybox có sẵn trên Ubuntu 20.04, và rất nhiều nơi&nbsp;khác.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Test nhanh tốc độ cpu dùng Python2021-09-15T00:00:00+07:002021-09-15T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-09-15:/speedtest.html<p>https://cpu.pymi.vn/ cho ta một cách để so sánh các <span class="caps">CPU</span> với nhau, đồng thời nắm được tốc độ của Python. Nhưng khi cần test nhanh tốc độ <span class="caps">CPU</span> trên máy mà ko muốn tải/code nhiều thì làm&nbsp;sao?</p> +<p>Việc làm này rất hữu dụng khi test …</p><p>https://cpu.pymi.vn/ cho ta một cách để so sánh các <span class="caps">CPU</span> với nhau, đồng thời nắm được tốc độ của Python. Nhưng khi cần test nhanh tốc độ <span class="caps">CPU</span> trên máy mà ko muốn tải/code nhiều thì làm&nbsp;sao?</p> +<p>Việc làm này rất hữu dụng khi test <span class="caps">CPU</span> của các máy ảo (cloud <span class="caps">VM</span>) - mặc dù ghi có 1 <span class="caps">CPU</span>, nhưng ko phải <span class="caps">CPU</span> nào cũng mạnh như&nbsp;nhau.</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1593697703081-129cf07ed377?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MzE3MjA3MDI&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Python có sẵn thư viện timeit dùng để đo tốc độ code chạy, +kết hợp với tính sum(range(N)) để đo tốc độ của 1 <span class="caps">CPU</span> trên&nbsp;máy.</p> +<div class="highlight"><pre><span></span><code>$ python3 -m timeit <span class="s1">&#39;sum(range(100_000_000))&#39;</span> +<span class="m">1</span> loop, best of <span class="m">5</span>: <span class="m">962</span> msec per loop +</code></pre></div> + +<p>962ms (0.952s) trên <span class="caps">CPU</span> <span class="caps">AMD</span> Ryzen 3 4300U, để tính tổng của range này, chú ý cả 2 function này đề viết bằng C nên rất nhanh so với loop bằng&nbsp;Python.</p> +<p><span class="caps">PS</span>: phiên bản Python cũng sẽ ảnh hưởng đến kết quả, vd 3.7 vs 3.10, và để chắc ăn nhớ chạy vài&nbsp;lần.</p> +<p>Còn&nbsp;bạn?</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Dịch ngược mã máy Python: import2021-09-06T00:00:00+07:002021-09-06T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-09-06:/disimport.html<p>Tiếp loạt bài về <a href="https://n.pymi.vn/compile.html">CPython&nbsp;compiler</a></p> +<p><img alt="img" src="https://images.unsplash.com/photo-1519750783826-e2420f4d687f?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MzA5NDEwMzg&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<div class="highlight"><pre><span></span><code> <span class="n">rand</span><span class="o">.</span><span class="n">py</span> + <span class="mi">1</span> <span class="kn">import</span> <span class="nn">random</span> + <span class="mi">2</span> <span class="n">do</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="s1">&#39;an&#39;</span><span class="p">,</span> <span class="s1">&#39;ngu&#39;</span><span class="p">])</span> + <span class="mi">3</span> <span class="nb">print</span><span class="p">(</span><span class="n">do</span><span class="p">)</span> + <span class="mi">4</span> + <span class="mi">5</span> <span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">timedelta</span> + <span class="mi">6</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Mot ngay co: </span><span class="si">{}</span><span class="s2">s&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">timedelta</span><span class="p">(</span><span class="n">days</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span><span class="o">.</span><span class="n">total_seconds</span><span class="p">()))</span> + <span class="mi">7</span> + <span class="mi">8</span> <span class="kn">from</span> <span class="nn">math</span> <span class="kn">import</span> <span class="o">*</span> + <span class="mi">9</span> <span class="nb">print</span><span class="p">(</span><span class="n">factorial</span><span class="p">(</span><span class="mi">5</span><span class="p">))</span> +</code></pre></div> + +<p>3 kiểu import trong …</p><p>Tiếp loạt bài về <a href="https://n.pymi.vn/compile.html">CPython&nbsp;compiler</a></p> +<p><img alt="img" src="https://images.unsplash.com/photo-1519750783826-e2420f4d687f?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MzA5NDEwMzg&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<div class="highlight"><pre><span></span><code> <span class="n">rand</span><span class="o">.</span><span class="n">py</span> + <span class="mi">1</span> <span class="kn">import</span> <span class="nn">random</span> + <span class="mi">2</span> <span class="n">do</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="s1">&#39;an&#39;</span><span class="p">,</span> <span class="s1">&#39;ngu&#39;</span><span class="p">])</span> + <span class="mi">3</span> <span class="nb">print</span><span class="p">(</span><span class="n">do</span><span class="p">)</span> + <span class="mi">4</span> + <span class="mi">5</span> <span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">timedelta</span> + <span class="mi">6</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Mot ngay co: </span><span class="si">{}</span><span class="s2">s&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">timedelta</span><span class="p">(</span><span class="n">days</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span><span class="o">.</span><span class="n">total_seconds</span><span class="p">()))</span> + <span class="mi">7</span> + <span class="mi">8</span> <span class="kn">from</span> <span class="nn">math</span> <span class="kn">import</span> <span class="o">*</span> + <span class="mi">9</span> <span class="nb">print</span><span class="p">(</span><span class="n">factorial</span><span class="p">(</span><span class="mi">5</span><span class="p">))</span> +</code></pre></div> + +<p>3 kiểu import trong Python. +Chạy&nbsp;dis:</p> +<div class="highlight"><pre><span></span><code><span class="c1"># python3 -m dis rand.py</span> + <span class="mi">1</span> <span class="mi">0</span> <span class="n">LOAD_CONST</span> <span class="mi">0</span> <span class="p">(</span><span class="mi">0</span><span class="p">)</span> + <span class="mi">2</span> <span class="n">LOAD_CONST</span> <span class="mi">1</span> <span class="p">(</span><span class="kc">None</span><span class="p">)</span> + <span class="mi">4</span> <span class="n">IMPORT_NAME</span> <span class="mi">0</span> <span class="p">(</span><span class="n">random</span><span class="p">)</span> + <span class="mi">6</span> <span class="n">STORE_NAME</span> <span class="mi">0</span> <span class="p">(</span><span class="n">random</span><span class="p">)</span> + + <span class="mi">2</span> <span class="mi">8</span> <span class="n">LOAD_NAME</span> <span class="mi">0</span> <span class="p">(</span><span class="n">random</span><span class="p">)</span> + <span class="mi">10</span> <span class="n">LOAD_METHOD</span> <span class="mi">1</span> <span class="p">(</span><span class="n">choice</span><span class="p">)</span> + <span class="mi">12</span> <span class="n">LOAD_CONST</span> <span class="mi">2</span> <span class="p">(</span><span class="s1">&#39;an&#39;</span><span class="p">)</span> + <span class="mi">14</span> <span class="n">LOAD_CONST</span> <span class="mi">3</span> <span class="p">(</span><span class="s1">&#39;ngu&#39;</span><span class="p">)</span> + <span class="mi">16</span> <span class="n">BUILD_LIST</span> <span class="mi">2</span> + <span class="mi">18</span> <span class="n">CALL_METHOD</span> <span class="mi">1</span> + <span class="mi">20</span> <span class="n">STORE_NAME</span> <span class="mi">2</span> <span class="p">(</span><span class="n">do</span><span class="p">)</span> + + <span class="mi">3</span> <span class="mi">22</span> <span class="n">LOAD_NAME</span> <span class="mi">3</span> <span class="p">(</span><span class="nb">print</span><span class="p">)</span> + <span class="mi">24</span> <span class="n">LOAD_NAME</span> <span class="mi">2</span> <span class="p">(</span><span class="n">do</span><span class="p">)</span> + <span class="mi">26</span> <span class="n">CALL_FUNCTION</span> <span class="mi">1</span> + <span class="mi">28</span> <span class="n">POP_TOP</span> + + <span class="mi">5</span> <span class="mi">30</span> <span class="n">LOAD_CONST</span> <span class="mi">0</span> <span class="p">(</span><span class="mi">0</span><span class="p">)</span> + <span class="mi">32</span> <span class="n">LOAD_CONST</span> <span class="mi">4</span> <span class="p">((</span><span class="s1">&#39;timedelta&#39;</span><span class="p">,))</span> + <span class="mi">34</span> <span class="n">IMPORT_NAME</span> <span class="mi">4</span> <span class="p">(</span><span class="n">datetime</span><span class="p">)</span> + <span class="mi">36</span> <span class="n">IMPORT_FROM</span> <span class="mi">5</span> <span class="p">(</span><span class="n">timedelta</span><span class="p">)</span> + <span class="mi">38</span> <span class="n">STORE_NAME</span> <span class="mi">5</span> <span class="p">(</span><span class="n">timedelta</span><span class="p">)</span> + <span class="mi">40</span> <span class="n">POP_TOP</span> + + <span class="mi">6</span> <span class="mi">42</span> <span class="n">LOAD_NAME</span> <span class="mi">3</span> <span class="p">(</span><span class="nb">print</span><span class="p">)</span> + <span class="mi">44</span> <span class="n">LOAD_CONST</span> <span class="mi">5</span> <span class="p">(</span><span class="s1">&#39;Mot ngay co: </span><span class="si">{}</span><span class="s1">s&#39;</span><span class="p">)</span> + <span class="mi">46</span> <span class="n">LOAD_METHOD</span> <span class="mi">6</span> <span class="p">(</span><span class="nb">format</span><span class="p">)</span> + <span class="mi">48</span> <span class="n">LOAD_NAME</span> <span class="mi">5</span> <span class="p">(</span><span class="n">timedelta</span><span class="p">)</span> + <span class="mi">50</span> <span class="n">LOAD_CONST</span> <span class="mi">6</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> + <span class="mi">52</span> <span class="n">LOAD_CONST</span> <span class="mi">7</span> <span class="p">((</span><span class="s1">&#39;days&#39;</span><span class="p">,))</span> + <span class="mi">54</span> <span class="n">CALL_FUNCTION_KW</span> <span class="mi">1</span> + <span class="mi">56</span> <span class="n">LOAD_METHOD</span> <span class="mi">7</span> <span class="p">(</span><span class="n">total_seconds</span><span class="p">)</span> + <span class="mi">58</span> <span class="n">CALL_METHOD</span> <span class="mi">0</span> + <span class="mi">60</span> <span class="n">CALL_METHOD</span> <span class="mi">1</span> + <span class="mi">62</span> <span class="n">CALL_FUNCTION</span> <span class="mi">1</span> + <span class="mi">64</span> <span class="n">POP_TOP</span> + + <span class="mi">8</span> <span class="mi">66</span> <span class="n">LOAD_CONST</span> <span class="mi">0</span> <span class="p">(</span><span class="mi">0</span><span class="p">)</span> + <span class="mi">68</span> <span class="n">LOAD_CONST</span> <span class="mi">8</span> <span class="p">((</span><span class="s1">&#39;*&#39;</span><span class="p">,))</span> + <span class="mi">70</span> <span class="n">IMPORT_NAME</span> <span class="mi">8</span> <span class="p">(</span><span class="n">math</span><span class="p">)</span> + <span class="mi">72</span> <span class="n">IMPORT_STAR</span> + + <span class="mi">9</span> <span class="mi">74</span> <span class="n">LOAD_NAME</span> <span class="mi">3</span> <span class="p">(</span><span class="nb">print</span><span class="p">)</span> + <span class="mi">76</span> <span class="n">LOAD_NAME</span> <span class="mi">9</span> <span class="p">(</span><span class="n">factorial</span><span class="p">)</span> + <span class="mi">78</span> <span class="n">LOAD_CONST</span> <span class="mi">9</span> <span class="p">(</span><span class="mi">5</span><span class="p">)</span> + <span class="mi">80</span> <span class="n">CALL_FUNCTION</span> <span class="mi">1</span> + <span class="mi">82</span> <span class="n">CALL_FUNCTION</span> <span class="mi">1</span> + <span class="mi">84</span> <span class="n">POP_TOP</span> + <span class="mi">86</span> <span class="n">LOAD_CONST</span> <span class="mi">1</span> <span class="p">(</span><span class="kc">None</span><span class="p">)</span> + <span class="mi">88</span> <span class="n">RETURN_VALUE</span> +</code></pre></div> + +<p><code>import random</code> sẽ dùng <span class="caps">BYTECODE</span> <code>IMPORT_NAME</code>, random sẽ trở thành 1 name +trong module này, được chứa <code>STORE_NAME</code> tương tự khi đặt <code>x = 5</code>. +Để gọi random.choice, đầu tiên phải +<code>LOAD_NAME</code> random rồi <code>LOAD_METHOD</code> choice.</p> +<p>Code <code>from datetime import timedelta</code>, chạy <code>IMPORT_NAME</code> datetime, nhưng không +<code>STORE_NAME</code> này mà <code>IMPORT_FROM</code> timedelta rồi <code>STORE_NAME</code> timedelta.</p> +<p>Code <code>from math import *</code> sử dụng <span class="caps">BYTECODE</span> <code>IMPORT_STAR</code>.</p> +<p>Chú ý khi gọi <code>random.choice</code> thì <span class="caps">BYTECODE</span> là <code>CALL_METHOD</code> do choice gắn liền +vào random - xem như 1 method của 1 module object, nhưng khi gọi +<code>factorial</code> đã <code>from math import *</code> thì <span class="caps">BYTECODE</span> là <code>CALL_FUNCTION</code>.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Dịch ngược mã máy Python: function/lambda2021-09-04T00:00:00+07:002021-09-04T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-09-04:/disfun.html<p>Tiếp loạt bài về <a href="https://n.pymi.vn/compile.html">CPython compiler</a> +<img alt="img" src="https://images.unsplash.com/photo-1601100521677-598dc05293c5?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MzA3MjU5MTM&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Code định nghĩa 2 function dùng def và&nbsp;lambda:</p> +<div class="highlight"><pre><span></span><code><span class="c1"># $ cat -n fun.py</span> + <span class="mi">1</span> <span class="k">def</span> <span class="nf">sum_two</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="mi">2</span> <span class="n">z</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span> + <span class="mi">3</span> <span class="k">return</span> <span class="n">z</span> + <span class="mi">4</span> + <span class="mi">5</span> <span class="n">a</span> <span class="o">=</span> <span class="mi">5</span> + <span class="mi">6</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">7</span> + <span class="mi">7</span> <span class="n">sum_two</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="mi">8</span> + <span class="mi">9</span> <span class="n">double</span> <span class="o">=</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="mi">2</span> + <span class="mi">10 …</span></code></pre></div><p>Tiếp loạt bài về <a href="https://n.pymi.vn/compile.html">CPython compiler</a> +<img alt="img" src="https://images.unsplash.com/photo-1601100521677-598dc05293c5?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MzA3MjU5MTM&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Code định nghĩa 2 function dùng def và&nbsp;lambda:</p> +<div class="highlight"><pre><span></span><code><span class="c1"># $ cat -n fun.py</span> + <span class="mi">1</span> <span class="k">def</span> <span class="nf">sum_two</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="mi">2</span> <span class="n">z</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span> + <span class="mi">3</span> <span class="k">return</span> <span class="n">z</span> + <span class="mi">4</span> + <span class="mi">5</span> <span class="n">a</span> <span class="o">=</span> <span class="mi">5</span> + <span class="mi">6</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">7</span> + <span class="mi">7</span> <span class="n">sum_two</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="mi">8</span> + <span class="mi">9</span> <span class="n">double</span> <span class="o">=</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="mi">2</span> + <span class="mi">10</span> <span class="n">double</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span> +</code></pre></div> + +<p>Chạy dis. Việc tạo function dùng def hay lambda đều sử dụng +<span class="caps">BYTECODE</span> <code>MAKE_FUNCTION</code> sau đó <code>STORE_NAME</code></p> +<div class="highlight"><pre><span></span><code> <span class="mi">1</span> <span class="mi">0</span> <span class="n">LOAD_CONST</span> <span class="mi">0</span> <span class="p">(</span><span class="o">&lt;</span><span class="n">code</span> <span class="nb">object</span> <span class="n">sum_two</span> <span class="n">at</span> <span class="mh">0x7fc64dcca190</span><span class="p">,</span> <span class="n">file</span> <span class="s2">&quot;fun.py&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="o">&gt;</span><span class="p">)</span> + <span class="mi">2</span> <span class="n">LOAD_CONST</span> <span class="mi">1</span> <span class="p">(</span><span class="s1">&#39;sum_two&#39;</span><span class="p">)</span> + <span class="mi">4</span> <span class="n">MAKE_FUNCTION</span> <span class="mi">0</span> + <span class="mi">6</span> <span class="n">STORE_NAME</span> <span class="mi">0</span> <span class="p">(</span><span class="n">sum_two</span><span class="p">)</span> + + <span class="mi">5</span> <span class="mi">8</span> <span class="n">LOAD_CONST</span> <span class="mi">2</span> <span class="p">(</span><span class="mi">5</span><span class="p">)</span> + <span class="mi">10</span> <span class="n">STORE_NAME</span> <span class="mi">1</span> <span class="p">(</span><span class="n">a</span><span class="p">)</span> + + <span class="mi">6</span> <span class="mi">12</span> <span class="n">LOAD_CONST</span> <span class="mi">3</span> <span class="p">(</span><span class="mi">7</span><span class="p">)</span> + <span class="mi">14</span> <span class="n">STORE_NAME</span> <span class="mi">2</span> <span class="p">(</span><span class="n">b</span><span class="p">)</span> + + <span class="mi">7</span> <span class="mi">16</span> <span class="n">LOAD_NAME</span> <span class="mi">0</span> <span class="p">(</span><span class="n">sum_two</span><span class="p">)</span> + <span class="mi">18</span> <span class="n">LOAD_NAME</span> <span class="mi">1</span> <span class="p">(</span><span class="n">a</span><span class="p">)</span> + <span class="mi">20</span> <span class="n">LOAD_NAME</span> <span class="mi">2</span> <span class="p">(</span><span class="n">b</span><span class="p">)</span> + <span class="mi">22</span> <span class="n">CALL_FUNCTION</span> <span class="mi">2</span> + <span class="mi">24</span> <span class="n">POP_TOP</span> + + <span class="mi">9</span> <span class="mi">26</span> <span class="n">LOAD_CONST</span> <span class="mi">4</span> <span class="p">(</span><span class="o">&lt;</span><span class="n">code</span> <span class="nb">object</span> <span class="o">&lt;</span><span class="k">lambda</span><span class="o">&gt;</span> <span class="n">at</span> <span class="mh">0x7fc64dcca2f0</span><span class="p">,</span> <span class="n">file</span> <span class="s2">&quot;fun.py&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">9</span><span class="o">&gt;</span><span class="p">)</span> + <span class="mi">28</span> <span class="n">LOAD_CONST</span> <span class="mi">5</span> <span class="p">(</span><span class="s1">&#39;&lt;lambda&gt;&#39;</span><span class="p">)</span> + <span class="mi">30</span> <span class="n">MAKE_FUNCTION</span> <span class="mi">0</span> + <span class="mi">32</span> <span class="n">STORE_NAME</span> <span class="mi">3</span> <span class="p">(</span><span class="n">double</span><span class="p">)</span> + + <span class="mi">10</span> <span class="mi">34</span> <span class="n">LOAD_NAME</span> <span class="mi">3</span> <span class="p">(</span><span class="n">double</span><span class="p">)</span> + <span class="mi">36</span> <span class="n">LOAD_CONST</span> <span class="mi">2</span> <span class="p">(</span><span class="mi">5</span><span class="p">)</span> + <span class="mi">38</span> <span class="n">CALL_FUNCTION</span> <span class="mi">1</span> + <span class="mi">40</span> <span class="n">POP_TOP</span> + <span class="mi">42</span> <span class="n">LOAD_CONST</span> <span class="mi">6</span> <span class="p">(</span><span class="kc">None</span><span class="p">)</span> + <span class="mi">44</span> <span class="n">RETURN_VALUE</span> + +<span class="n">Disassembly</span> <span class="n">of</span> <span class="o">&lt;</span><span class="n">code</span> <span class="nb">object</span> <span class="n">sum_two</span> <span class="n">at</span> <span class="mh">0x7fc64dcca190</span><span class="p">,</span> <span class="n">file</span> <span class="s2">&quot;fun.py&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="o">&gt;</span><span class="p">:</span> + <span class="mi">2</span> <span class="mi">0</span> <span class="n">LOAD_FAST</span> <span class="mi">0</span> <span class="p">(</span><span class="n">x</span><span class="p">)</span> + <span class="mi">2</span> <span class="n">LOAD_FAST</span> <span class="mi">1</span> <span class="p">(</span><span class="n">y</span><span class="p">)</span> + <span class="mi">4</span> <span class="n">BINARY_ADD</span> + <span class="mi">6</span> <span class="n">STORE_FAST</span> <span class="mi">2</span> <span class="p">(</span><span class="n">z</span><span class="p">)</span> + + <span class="mi">3</span> <span class="mi">8</span> <span class="n">LOAD_FAST</span> <span class="mi">2</span> <span class="p">(</span><span class="n">z</span><span class="p">)</span> + <span class="mi">10</span> <span class="n">RETURN_VALUE</span> + +<span class="n">Disassembly</span> <span class="n">of</span> <span class="o">&lt;</span><span class="n">code</span> <span class="nb">object</span> <span class="o">&lt;</span><span class="k">lambda</span><span class="o">&gt;</span> <span class="n">at</span> <span class="mh">0x7fc64dcca2f0</span><span class="p">,</span> <span class="n">file</span> <span class="s2">&quot;fun.py&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">9</span><span class="o">&gt;</span><span class="p">:</span> + <span class="mi">9</span> <span class="mi">0</span> <span class="n">LOAD_FAST</span> <span class="mi">0</span> <span class="p">(</span><span class="n">x</span><span class="p">)</span> + <span class="mi">2</span> <span class="n">LOAD_CONST</span> <span class="mi">1</span> <span class="p">(</span><span class="mi">2</span><span class="p">)</span> + <span class="mi">4</span> <span class="n">BINARY_MULTIPLY</span> + <span class="mi">6</span> <span class="n">RETURN_VALUE</span> +</code></pre></div> + +<p>khác với các khái niệm trước, khi viết function sẽ thấy python dis riêng ra từng mục cho từng&nbsp;function.</p> +<p>Với function sum_two tại dòng 1, có riêng&nbsp;mục</p> +<p><code>Disassembly of &lt;code object sum_two at 0x7fc64dcca190, file "fun.py", line 1&gt;:</code></p> +<p>chú ý thêm sự khác biệt khi dùng biến trong function sử dụng <code>LOAD_FAST</code> thay vì <code>LOAD_NAME</code>, tạo biến sử dụng <code>STORE_FAST</code> thay <code>STORE_NAME</code>. function kết thúc bằng việc <code>RETURN_VALUE</code>.</p> +<p>Thế nhưng.. return cái gì? câu <code>RETURN_VALUE</code> không thấy ghi thêm gì sau, làm +sao biết nó return gì? +<code>RETURN_VALUE</code> sẽ return giá trị cuối cùng tính toán được. Như trong <code>sum_two</code> +sẽ return giá trị của z sau khi <code>LOAD_FAST</code>. Với <code>lambda</code> function, return +kết quả của phép nhân <code>BINARY_MULTIPLY</code>. Cụ thể hơn, trong Python <span class="caps">VM</span> gọi đây là +<span class="caps">TOP</span> <span class="caps">OF</span> <span class="caps">STACK</span> (<span class="caps">TOS</span>), kết quả của giá trị tính toán xong sẽ luôn nằm ở đây. +Vậy nếu muốn return giá trị không phải là tính cuối cùng thì&nbsp;sao?</p> +<p>Thử nghiệm với 2 function không trả về giá trị vừa tính&nbsp;xong:</p> +<div class="highlight"><pre><span></span><code> <span class="mi">1</span> <span class="k">def</span> <span class="nf">r_none</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="mi">2</span> <span class="n">z</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span> + <span class="mi">3</span> <span class="k">return</span> + <span class="mi">4</span> + <span class="mi">5</span> + <span class="mi">6</span> <span class="k">def</span> <span class="nf">r_i</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="mi">7</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">8</span> + <span class="mi">8</span> <span class="n">z</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span> + <span class="mi">9</span> <span class="k">return</span> <span class="n">i</span> +</code></pre></div> + +<p>Kết quả&nbsp;dis</p> +<div class="highlight"><pre><span></span><code><span class="n">Disassembly</span> <span class="n">of</span> <span class="o">&lt;</span><span class="n">code</span> <span class="nb">object</span> <span class="n">r_none</span> <span class="n">at</span> <span class="mh">0x7fdd4b47bdf0</span><span class="p">,</span> <span class="n">file</span> <span class="s2">&quot;fun.py&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="o">&gt;</span><span class="p">:</span> + <span class="mi">2</span> <span class="mi">0</span> <span class="n">LOAD_FAST</span> <span class="mi">0</span> <span class="p">(</span><span class="n">x</span><span class="p">)</span> + <span class="mi">2</span> <span class="n">LOAD_FAST</span> <span class="mi">1</span> <span class="p">(</span><span class="n">y</span><span class="p">)</span> + <span class="mi">4</span> <span class="n">BINARY_ADD</span> + <span class="mi">6</span> <span class="n">STORE_FAST</span> <span class="mi">2</span> <span class="p">(</span><span class="n">z</span><span class="p">)</span> + + <span class="mi">3</span> <span class="mi">8</span> <span class="n">LOAD_CONST</span> <span class="mi">0</span> <span class="p">(</span><span class="kc">None</span><span class="p">)</span> + <span class="mi">10</span> <span class="n">RETURN_VALUE</span> + +<span class="n">Disassembly</span> <span class="n">of</span> <span class="o">&lt;</span><span class="n">code</span> <span class="nb">object</span> <span class="n">r_i</span> <span class="n">at</span> <span class="mh">0x7fdd4b47f030</span><span class="p">,</span> <span class="n">file</span> <span class="s2">&quot;fun.py&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">6</span><span class="o">&gt;</span><span class="p">:</span> + <span class="mi">7</span> <span class="mi">0</span> <span class="n">LOAD_CONST</span> <span class="mi">1</span> <span class="p">(</span><span class="mi">8</span><span class="p">)</span> + <span class="mi">2</span> <span class="n">STORE_FAST</span> <span class="mi">2</span> <span class="p">(</span><span class="n">i</span><span class="p">)</span> + + <span class="mi">8</span> <span class="mi">4</span> <span class="n">LOAD_FAST</span> <span class="mi">0</span> <span class="p">(</span><span class="n">x</span><span class="p">)</span> + <span class="mi">6</span> <span class="n">LOAD_FAST</span> <span class="mi">1</span> <span class="p">(</span><span class="n">y</span><span class="p">)</span> + <span class="mi">8</span> <span class="n">BINARY_ADD</span> + <span class="mi">10</span> <span class="n">STORE_FAST</span> <span class="mi">3</span> <span class="p">(</span><span class="n">z</span><span class="p">)</span> + + <span class="mi">9</span> <span class="mi">12</span> <span class="n">LOAD_FAST</span> <span class="mi">2</span> <span class="p">(</span><span class="n">i</span><span class="p">)</span> + <span class="mi">14</span> <span class="n">RETURN_VALUE</span> +</code></pre></div> + +<p>function <code>r_none</code> load giá trị None để return vì code chỉ ghi return không gì cả. +function <code>r_i</code> sẽ <code>LOAD_FAST</code> giá trị <code>i</code> rồi return&nbsp;i.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Python làm chủ mọi cuộc chơi ieee 20212021-09-03T00:00:00+07:002021-09-03T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-09-03:/ieee2021.html<p>Bảng xếp hạng của <span class="caps">IEEE</span> năm 2021 về mức độ phổ biến, Python lại một lần nữa đứng số 1, +ở mọi tiêu chí sắp&nbsp;xếp.</p> +<p>Từ lập trình nhúng, tới lập trình web, tới hệ thống <span class="caps">AI</span> cỡ siêu to khổng lồ, +Python xuất hiện ở mọi&nbsp;nơi.</p> +<p><img alt="img" src="https://spectrum.ieee.org/media-library/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpbWFnZSI6Imh0dHBzOi8vYXNzZXRzLnJibC5tcy8yNzI2MDM0NS9vcmlnaW4uanBnIiwiZXhwaXJlc19hdCI6MTY3NTQxMjE1N30.En-9vYPrZYXKbMY9GpAn6qMPY6PDLfq_2iOxOHaTtxc/image.jpg?width=600&amp;height=450"></p> +<h2>Link …</h2><p>Bảng xếp hạng của <span class="caps">IEEE</span> năm 2021 về mức độ phổ biến, Python lại một lần nữa đứng số 1, +ở mọi tiêu chí sắp&nbsp;xếp.</p> +<p>Từ lập trình nhúng, tới lập trình web, tới hệ thống <span class="caps">AI</span> cỡ siêu to khổng lồ, +Python xuất hiện ở mọi&nbsp;nơi.</p> +<p><img alt="img" src="https://spectrum.ieee.org/media-library/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpbWFnZSI6Imh0dHBzOi8vYXNzZXRzLnJibC5tcy8yNzI2MDM0NS9vcmlnaW4uanBnIiwiZXhwaXJlc19hdCI6MTY3NTQxMjE1N30.En-9vYPrZYXKbMY9GpAn6qMPY6PDLfq_2iOxOHaTtxc/image.jpg?width=600&amp;height=450"></p> +<h2>Link</h2> +<p>Nếu muốn biết ai là số 2 thì bấm vào xem link&nbsp;nhé</p> +<ul> +<li><a href="https://spectrum.ieee.org/top-programming-languages-2021">https://spectrum.ieee.org/top-programming-languages-2021</a></li> +<li><a href="https://spectrum.ieee.org/top-programming-languages">https://spectrum.ieee.org/top-programming-languages/</a></li> +</ul> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Dịch ngược mã máy Python: for/while2021-09-02T00:00:00+07:002021-09-02T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-09-02:/disfor.html<p>Tiếp loạt bài về <a href="https://n.pymi.vn/compile.html">CPython&nbsp;compiler</a></p> +<p><img alt="img" src="https://images.unsplash.com/photo-1612283592698-6edf01b1edc7?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MzA1NTAzNjk&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Xem ví dụ tính tổng 1 tới 100&nbsp;sau:</p> +<h3>for</h3> +<div class="highlight"><pre><span></span><code><span class="c1"># for.py</span> +<span class="n">t</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="mi">1</span><span class="p">,</span> <span class="mi">101</span><span class="p">):</span> + <span class="n">t</span> <span class="o">=</span> <span class="n">t</span> <span class="o">+</span> <span class="n">i</span> + +<span class="nb">print</span><span class="p">(</span><span class="n">t</span><span class="p">)</span> +</code></pre></div> + +<p>Chạy&nbsp;dis</p> +<div class="highlight"><pre><span></span><code>$ python3 -m dis <span class="k">for</span>.py + <span class="m">1</span> <span class="m">0</span> LOAD_CONST <span class="m">0</span> <span class="o">(</span><span class="m">0</span><span class="o">)</span> + <span class="m">2</span> STORE_NAME <span class="m">0</span> <span class="o">(</span>t<span class="o">)</span> + + <span class="m">2</span> <span class="m">4</span> LOAD_NAME …</code></pre></div><p>Tiếp loạt bài về <a href="https://n.pymi.vn/compile.html">CPython&nbsp;compiler</a></p> +<p><img alt="img" src="https://images.unsplash.com/photo-1612283592698-6edf01b1edc7?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MzA1NTAzNjk&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Xem ví dụ tính tổng 1 tới 100&nbsp;sau:</p> +<h3>for</h3> +<div class="highlight"><pre><span></span><code><span class="c1"># for.py</span> +<span class="n">t</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="mi">1</span><span class="p">,</span> <span class="mi">101</span><span class="p">):</span> + <span class="n">t</span> <span class="o">=</span> <span class="n">t</span> <span class="o">+</span> <span class="n">i</span> + +<span class="nb">print</span><span class="p">(</span><span class="n">t</span><span class="p">)</span> +</code></pre></div> + +<p>Chạy&nbsp;dis</p> +<div class="highlight"><pre><span></span><code>$ python3 -m dis <span class="k">for</span>.py + <span class="m">1</span> <span class="m">0</span> LOAD_CONST <span class="m">0</span> <span class="o">(</span><span class="m">0</span><span class="o">)</span> + <span class="m">2</span> STORE_NAME <span class="m">0</span> <span class="o">(</span>t<span class="o">)</span> + + <span class="m">2</span> <span class="m">4</span> LOAD_NAME <span class="m">1</span> <span class="o">(</span>range<span class="o">)</span> + <span class="m">6</span> LOAD_CONST <span class="m">1</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span> + <span class="m">8</span> LOAD_CONST <span class="m">2</span> <span class="o">(</span><span class="m">101</span><span class="o">)</span> + <span class="m">10</span> CALL_FUNCTION <span class="m">2</span> + <span class="m">12</span> GET_ITER + &gt;&gt; <span class="m">14</span> FOR_ITER <span class="m">12</span> <span class="o">(</span>to <span class="m">28</span><span class="o">)</span> + <span class="m">16</span> STORE_NAME <span class="m">2</span> <span class="o">(</span>i<span class="o">)</span> + + <span class="m">3</span> <span class="m">18</span> LOAD_NAME <span class="m">0</span> <span class="o">(</span>t<span class="o">)</span> + <span class="m">20</span> LOAD_NAME <span class="m">2</span> <span class="o">(</span>i<span class="o">)</span> + <span class="m">22</span> BINARY_ADD + <span class="m">24</span> STORE_NAME <span class="m">0</span> <span class="o">(</span>t<span class="o">)</span> + <span class="m">26</span> JUMP_ABSOLUTE <span class="m">14</span> + + <span class="m">5</span> &gt;&gt; <span class="m">28</span> LOAD_NAME <span class="m">3</span> <span class="o">(</span>print<span class="o">)</span> + <span class="m">30</span> LOAD_NAME <span class="m">0</span> <span class="o">(</span>t<span class="o">)</span> + <span class="m">32</span> CALL_FUNCTION <span class="m">1</span> + <span class="m">34</span> POP_TOP + <span class="m">36</span> LOAD_CONST <span class="m">3</span> <span class="o">(</span>None<span class="o">)</span> + <span class="m">38</span> RETURN_VALUE +</code></pre></div> + +<p>sau khi <span class="caps">LOAD</span> xong function range và 2 tham số 1, 101, <code>CALL_FUNCTION</code> gọi function <code>range</code>.</p> +<p>Tại offset 12 có <span class="caps">BYTECODE</span> <code>GET_ITER</code> để lấy iterator từ object&nbsp;range.</p> +<p>offset 14 có đánh dấu vị trí jump <code>&gt;&gt;</code>, bắt đầu thực hiện +<code>FOR_ITER</code>, với đầu vào là iterator lấy ở offset 12, khi thực hiện xong việc lặp sẽ nhảy tới offset 28 (ngoài vòng&nbsp;for).</p> +<p>Code trong thân vòng lặp for kết thúc bằng <span class="caps">BYTECODE</span> <code>JUMP_ABSOLUTE</code> và nhảy tới offset 14, tức chạy xong thân vòng lặp thì chuyển tới vòng lặp tiếp&nbsp;theo.</p> +<h3>while</h3> +<div class="highlight"><pre><span></span><code><span class="c1"># while.py</span> +<span class="n">t</span> <span class="o">=</span> <span class="mi">0</span> +<span class="n">i</span> <span class="o">=</span> <span class="mi">1</span> +<span class="k">while</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">101</span><span class="p">:</span> + <span class="n">t</span> <span class="o">=</span> <span class="n">t</span> <span class="o">+</span> <span class="n">i</span> + +<span class="nb">print</span><span class="p">(</span><span class="n">t</span><span class="p">)</span> +</code></pre></div> + +<p>Chạy&nbsp;dis</p> +<div class="highlight"><pre><span></span><code>$ python3 -m dis <span class="k">while</span>.py + <span class="m">1</span> <span class="m">0</span> LOAD_CONST <span class="m">0</span> <span class="o">(</span><span class="m">0</span><span class="o">)</span> + <span class="m">2</span> STORE_NAME <span class="m">0</span> <span class="o">(</span>t<span class="o">)</span> + + <span class="m">2</span> <span class="m">4</span> LOAD_CONST <span class="m">1</span> <span class="o">(</span><span class="m">1</span><span class="o">)</span> + <span class="m">6</span> STORE_NAME <span class="m">1</span> <span class="o">(</span>i<span class="o">)</span> + + <span class="m">3</span> &gt;&gt; <span class="m">8</span> LOAD_NAME <span class="m">1</span> <span class="o">(</span>i<span class="o">)</span> + <span class="m">10</span> LOAD_CONST <span class="m">2</span> <span class="o">(</span><span class="m">101</span><span class="o">)</span> + <span class="m">12</span> COMPARE_OP <span class="m">0</span> <span class="o">(</span>&lt;<span class="o">)</span> + <span class="m">14</span> POP_JUMP_IF_FALSE <span class="m">26</span> + + <span class="m">4</span> <span class="m">16</span> LOAD_NAME <span class="m">0</span> <span class="o">(</span>t<span class="o">)</span> + <span class="m">18</span> LOAD_NAME <span class="m">1</span> <span class="o">(</span>i<span class="o">)</span> + <span class="m">20</span> BINARY_ADD + <span class="m">22</span> STORE_NAME <span class="m">0</span> <span class="o">(</span>t<span class="o">)</span> + <span class="m">24</span> JUMP_ABSOLUTE <span class="m">8</span> + + <span class="m">6</span> &gt;&gt; <span class="m">26</span> LOAD_NAME <span class="m">2</span> <span class="o">(</span>print<span class="o">)</span> + <span class="m">28</span> LOAD_NAME <span class="m">0</span> <span class="o">(</span>t<span class="o">)</span> + <span class="m">30</span> CALL_FUNCTION <span class="m">1</span> + <span class="m">32</span> POP_TOP + <span class="m">34</span> LOAD_CONST <span class="m">3</span> <span class="o">(</span>None<span class="o">)</span> + <span class="m">36</span> RETURN_VALUE +</code></pre></div> + +<p>Điều kiện theo sau vòng lặp while sẽ khiến while nhảy tới ngoài vòng lặp khi điều kiện sai (như if nhảy sang else), +dùng cùng <span class="caps">BYTECODE</span> <code>POP_JUMP_IF_FALSE</code> tới offset 26 - vị trí ngoài vòng&nbsp;lặp.</p> +<p>Hết thân vòng lặp while, <code>JUMP_ABSOLUTE</code> lại nhảy tới vòng lặp tiếp&nbsp;theo.</p> +<h3>Kết&nbsp;luận</h3> +<p>for và while đều sinh ra <span class="caps">BYTECODE</span> tương tự nhau. +Trong khi for có xuất hiện 2 <span class="caps">BYTECODE</span> mới là FOR_ITER và GET_ITER thì while chỉ toàn là <span class="caps">JUMP</span>.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Dịch ngược mã máy Python: if/else2021-09-01T00:00:00+07:002021-09-01T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-09-01:/disif.html<p><img alt="img" src="https://images.unsplash.com/photo-1608634769432-f9b6524aa2bf?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MzA0NjE2MDE&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Tiếp loạt bài về <a href="https://n.pymi.vn/compile.html">CPython&nbsp;compiler</a></p> +<div class="highlight"><pre><span></span><code><span class="c1"># ifelse.py</span> +<span class="n">x</span> <span class="o">=</span> <span class="mi">20</span> +<span class="k">if</span> <span class="n">x</span> <span class="o">&gt;</span> <span class="mi">18</span><span class="p">:</span> + <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;18+&quot;</span><span class="p">)</span> +<span class="k">else</span><span class="p">:</span> + <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;vaccine is coming&quot;</span><span class="p">)</span> +</code></pre></div> + +<div class="highlight"><pre><span></span><code> $ python3 -m dis i + 1 0 LOAD_CONST 0 (20) + 2 STORE_NAME 0 (x) + + 2 4 LOAD_NAME 0 (x) + 6 LOAD_CONST 1 (18) + 8 COMPARE_OP 4 (&gt;) + 10 POP_JUMP_IF_FALSE 22 + + 3 12 …</code></pre></div><p><img alt="img" src="https://images.unsplash.com/photo-1608634769432-f9b6524aa2bf?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MzA0NjE2MDE&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Tiếp loạt bài về <a href="https://n.pymi.vn/compile.html">CPython&nbsp;compiler</a></p> +<div class="highlight"><pre><span></span><code><span class="c1"># ifelse.py</span> +<span class="n">x</span> <span class="o">=</span> <span class="mi">20</span> +<span class="k">if</span> <span class="n">x</span> <span class="o">&gt;</span> <span class="mi">18</span><span class="p">:</span> + <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;18+&quot;</span><span class="p">)</span> +<span class="k">else</span><span class="p">:</span> + <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;vaccine is coming&quot;</span><span class="p">)</span> +</code></pre></div> + +<div class="highlight"><pre><span></span><code> $ python3 -m dis i + 1 0 LOAD_CONST 0 (20) + 2 STORE_NAME 0 (x) + + 2 4 LOAD_NAME 0 (x) + 6 LOAD_CONST 1 (18) + 8 COMPARE_OP 4 (&gt;) + 10 POP_JUMP_IF_FALSE 22 + + 3 12 LOAD_NAME 1 (print) + 14 LOAD_CONST 2 (&#39;18+&#39;) + 16 CALL_FUNCTION 1 + 18 POP_TOP + 20 JUMP_FORWARD 8 (to 30) + + 5 &gt;&gt; 22 LOAD_NAME 1 (print) + 24 LOAD_CONST 3 (&#39;vaccine is coming&#39;) + 26 CALL_FUNCTION 1 + 28 POP_TOP + &gt;&gt; 30 LOAD_CONST 4 (None) + 32 RETURN_VALUE +</code></pre></div> + +<p>Code <code>LOAD_CONST</code> số 20, gán <code>x = 20</code>, +sau đó <code>LOAD_NAME</code> x và <code>LOAD_CONST</code> 18 để thực hiện so sánh. +So sánh sử dụng bytecode <code>COMPARE_OP</code>, biểu thức so sánh này nằm trong phần điều kiện của lệnh <code>if</code>. Xuất hiện <span class="caps">BYTECODE</span>&nbsp;mới:</p> +<p><code>POP_JUMP_IF_FALSE</code> chú ý số 22 theo sau nó. +Để ý trước mỗi <span class="caps">BYTECODE</span> là một con số. +Số này là <a href="https://docs.python.org/3/library/dis.html#dis.Instruction.offset"><strong>offset</strong></a> hiểu đơn giản là vị trí hay địa chỉ trong đoạn&nbsp;code.</p> +<div class="highlight"><pre><span></span><code> start index of operation within bytecode sequence +</code></pre></div> + +<p><code>POP_JUMP_IF_FALSE 22</code> nói rằng sẽ <span class="caps">JUMP</span> (nhảy) tới +offset 22 nếu sai. Offset 22 ở đây chính là code trong khối <code>else</code>.</p> +<p>Sau khi hết code trong khối <code>if</code>, xuất hiện <span class="caps">BYTECODE</span>: +<code>20 JUMP_FORWARD 8 (to 30)</code> +tức <span class="caps">JUMP</span> tới offset 30. Rõ ràng khi code trong <code>if</code> chạy hết thì Python sẽ bỏ qua phần code else và nhảy tới vị trí ngoài&nbsp;if/else.</p> +<p>Trong output của <code>dis</code>, các vị trí <span class="caps">JUMP</span> (jump target) được ký hiệu bởi dấu <code>&gt;&gt;</code>. Xem trong code của <a href="https://github.com/python/cpython/blob/3.9/Lib/dis.py#L245">dis</a></p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Hello dis2021-08-31T00:00:00+07:002021-08-31T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-08-31:/dis1.html<p>Tiếp loạt bài về Python&nbsp;compiler.</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1563775506308-5812e69b313e?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MzAzNzU1Njg&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600Wrote%20newpost.md"></p> +<p>Trong <a href="https://n.pymi.vn/compile.html">CPython compiler</a>, ta đã biết CPython có thực +hiện việc compile code, sinh ra các bytecode, rồi bytecode sẽ được CPython <span class="caps">VM</span> +chạy. Khi import module, CPython sinh ra các file <code>.pyc</code> sau khi compile. Nội dung +các file này ở dạng binary …</p><p>Tiếp loạt bài về Python&nbsp;compiler.</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1563775506308-5812e69b313e?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MzAzNzU1Njg&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600Wrote%20newpost.md"></p> +<p>Trong <a href="https://n.pymi.vn/compile.html">CPython compiler</a>, ta đã biết CPython có thực +hiện việc compile code, sinh ra các bytecode, rồi bytecode sẽ được CPython <span class="caps">VM</span> +chạy. Khi import module, CPython sinh ra các file <code>.pyc</code> sau khi compile. Nội dung +các file này ở dạng binary, người dùng sẽ không thể đọc&nbsp;được.</p> +<p>Python có sẵn thư viện <code>dis</code>, giúp thực hiện &#8220;disassembly&#8221;, biến code thành +dạng mã bytecode đọc được. +Bài này giới thiệu các phép toán đơn giản để làm quen: +Tạo các file code .py đơn giản rồi chạy chúng với lệnh: <code>python3 -m dis tenfile.py</code></p> +<div class="highlight"><pre><span></span><code><span class="c1"># ==&gt; d1.py &lt;==</span> +<span class="n">x</span> <span class="o">=</span> <span class="mi">5</span> + +<span class="c1"># ==&gt; d2.py &lt;==</span> +<span class="n">x</span> <span class="o">=</span> <span class="mi">5</span> +<span class="n">y</span> <span class="o">=</span> <span class="mi">7</span> + +<span class="c1"># ==&gt; d3.py &lt;==</span> +<span class="n">x</span> <span class="o">=</span> <span class="mi">5</span> +<span class="n">y</span> <span class="o">=</span> <span class="mi">7</span> +<span class="n">z</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span> + +<span class="c1"># ==&gt; d4.py &lt;==</span> +<span class="n">x</span> <span class="o">=</span> <span class="mi">5</span> +<span class="n">y</span> <span class="o">=</span> <span class="mi">7</span> +<span class="n">z</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span> +<span class="n">s</span> <span class="o">=</span> <span class="n">x</span> <span class="o">-</span> <span class="n">y</span> +<span class="nb">print</span><span class="p">(</span><span class="n">z</span><span class="p">)</span> +<span class="nb">print</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> +<span class="nb">print</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="s2">&quot;Python&quot;</span><span class="p">))</span> + +<span class="c1"># ==&gt; d5.py &lt;==</span> +<span class="n">x</span> <span class="o">=</span> <span class="mi">5</span> +<span class="n">y</span> <span class="o">=</span> <span class="o">-</span><span class="n">x</span> +</code></pre></div> + +<p>File đầu tiên gán x = 5, số 1 trong cột đầu tiên thể hiện +dòng&nbsp;code.</p> +<div class="highlight"><pre><span></span><code>$ python3 -m dis d1.py + <span class="m">1</span> <span class="m">0</span> LOAD_CONST <span class="m">0</span> <span class="o">(</span><span class="m">5</span><span class="o">)</span> + <span class="m">2</span> STORE_NAME <span class="m">0</span> <span class="o">(</span>x<span class="o">)</span> + <span class="m">4</span> LOAD_CONST <span class="m">1</span> <span class="o">(</span>None<span class="o">)</span> + <span class="m">6</span> RETURN_VALUE +</code></pre></div> + +<p>5 là một giá trị có sẵn, Python thực hiện việc lấy nó <code>LOAD_CONST</code>, sau đó gán x = 5 với <code>STORE_NAME</code>. 2 dòng sau tạm không bàn tới và xem tiếp các ví dụ còn&nbsp;lại:</p> +<p>chạy d2.py sẽ không khác với d1.py, chỉ thêm phần LOAD_CONST cho 7 và thưc hiện <code>STORE_NAME</code> y =&nbsp;7.</p> +<p>d3.py thêm một phép tính cộng, có <span class="caps">BYTECODE</span> là <code>BINARY_ADD</code>.</p> +<div class="highlight"><pre><span></span><code>d3.py + <span class="m">1</span> <span class="m">0</span> LOAD_CONST <span class="m">0</span> <span class="o">(</span><span class="m">5</span><span class="o">)</span> + <span class="m">2</span> STORE_NAME <span class="m">0</span> <span class="o">(</span>x<span class="o">)</span> + + <span class="m">2</span> <span class="m">4</span> LOAD_CONST <span class="m">1</span> <span class="o">(</span><span class="m">7</span><span class="o">)</span> + <span class="m">6</span> STORE_NAME <span class="m">1</span> <span class="o">(</span>y<span class="o">)</span> + + <span class="m">3</span> <span class="m">8</span> LOAD_NAME <span class="m">0</span> <span class="o">(</span>x<span class="o">)</span> + <span class="m">10</span> LOAD_NAME <span class="m">1</span> <span class="o">(</span>y<span class="o">)</span> + <span class="m">12</span> BINARY_ADD + <span class="m">14</span> STORE_NAME <span class="m">2</span> <span class="o">(</span>z<span class="o">)</span> + <span class="m">16</span> LOAD_CONST <span class="m">2</span> <span class="o">(</span>None<span class="o">)</span> + <span class="m">18</span> RETURN_VALUE +</code></pre></div> + +<p>d4.py có phép trừ, có <span class="caps">BYTECODE</span> là <code>BINARY_SUBTRACT</code>. +Chú ý chữ <code>BINARY</code> nói đây là phép toán có 2 toán tử như x + y hay x - y, một +loại phép toán khác chỉ có 1 toán tử như phép lấy số âm (-x), thì - là +<code>UNARY_NEGATIVE</code>.</p> +<div class="highlight"><pre><span></span><code>$ python3 -m dis d5.py + <span class="m">1</span> <span class="m">0</span> LOAD_CONST <span class="m">0</span> <span class="o">(</span><span class="m">5</span><span class="o">)</span> + <span class="m">2</span> STORE_NAME <span class="m">0</span> <span class="o">(</span>x<span class="o">)</span> + + <span class="m">2</span> <span class="m">4</span> LOAD_NAME <span class="m">0</span> <span class="o">(</span>x<span class="o">)</span> + <span class="m">6</span> UNARY_NEGATIVE + <span class="m">8</span> STORE_NAME <span class="m">1</span> <span class="o">(</span>y<span class="o">)</span> + <span class="m">10</span> LOAD_CONST <span class="m">1</span> <span class="o">(</span>None<span class="o">)</span> + <span class="m">12</span> RETURN_VALUE +<span class="c1"># d5.py</span> +<span class="nv">x</span> <span class="o">=</span> <span class="m">5</span> +<span class="nv">y</span> <span class="o">=</span> -x +</code></pre></div> + +<p>d4.py có gọi các function có sẵn, việc đầu tiên là lấy ra function với +<code>LOAD_NAME</code>, rồi gọi với <code>CALL_FUNCTION</code>. Chú ý sau mỗi lần gọi function có +<code>POP_TOP</code>, sẽ tìm hiểu ở bài&nbsp;sau.</p> +<div class="highlight"><pre><span></span><code><span class="n">d4</span><span class="o">.</span><span class="n">py</span> + <span class="mi">1</span> <span class="mi">0</span> <span class="n">LOAD_CONST</span> <span class="mi">0</span> <span class="p">(</span><span class="mi">5</span><span class="p">)</span> + <span class="mi">2</span> <span class="n">STORE_NAME</span> <span class="mi">0</span> <span class="p">(</span><span class="n">x</span><span class="p">)</span> + + <span class="mi">2</span> <span class="mi">4</span> <span class="n">LOAD_CONST</span> <span class="mi">1</span> <span class="p">(</span><span class="mi">7</span><span class="p">)</span> + <span class="mi">6</span> <span class="n">STORE_NAME</span> <span class="mi">1</span> <span class="p">(</span><span class="n">y</span><span class="p">)</span> + + <span class="mi">3</span> <span class="mi">8</span> <span class="n">LOAD_NAME</span> <span class="mi">0</span> <span class="p">(</span><span class="n">x</span><span class="p">)</span> + <span class="mi">10</span> <span class="n">LOAD_NAME</span> <span class="mi">1</span> <span class="p">(</span><span class="n">y</span><span class="p">)</span> + <span class="mi">12</span> <span class="n">BINARY_ADD</span> + <span class="mi">14</span> <span class="n">STORE_NAME</span> <span class="mi">2</span> <span class="p">(</span><span class="n">z</span><span class="p">)</span> + + <span class="mi">4</span> <span class="mi">16</span> <span class="n">LOAD_NAME</span> <span class="mi">0</span> <span class="p">(</span><span class="n">x</span><span class="p">)</span> + <span class="mi">18</span> <span class="n">LOAD_NAME</span> <span class="mi">1</span> <span class="p">(</span><span class="n">y</span><span class="p">)</span> + <span class="mi">20</span> <span class="n">BINARY_SUBTRACT</span> + <span class="mi">22</span> <span class="n">STORE_NAME</span> <span class="mi">3</span> <span class="p">(</span><span class="n">s</span><span class="p">)</span> + + <span class="mi">5</span> <span class="mi">24</span> <span class="n">LOAD_NAME</span> <span class="mi">4</span> <span class="p">(</span><span class="nb">print</span><span class="p">)</span> + <span class="mi">26</span> <span class="n">LOAD_NAME</span> <span class="mi">2</span> <span class="p">(</span><span class="n">z</span><span class="p">)</span> + <span class="mi">28</span> <span class="n">CALL_FUNCTION</span> <span class="mi">1</span> + <span class="mi">30</span> <span class="n">POP_TOP</span> + + <span class="mi">6</span> <span class="mi">32</span> <span class="n">LOAD_NAME</span> <span class="mi">4</span> <span class="p">(</span><span class="nb">print</span><span class="p">)</span> + <span class="mi">34</span> <span class="n">LOAD_NAME</span> <span class="mi">3</span> <span class="p">(</span><span class="n">s</span><span class="p">)</span> + <span class="mi">36</span> <span class="n">CALL_FUNCTION</span> <span class="mi">1</span> + <span class="mi">38</span> <span class="n">POP_TOP</span> + + <span class="mi">7</span> <span class="mi">40</span> <span class="n">LOAD_NAME</span> <span class="mi">4</span> <span class="p">(</span><span class="nb">print</span><span class="p">)</span> + <span class="mi">42</span> <span class="n">LOAD_NAME</span> <span class="mi">5</span> <span class="p">(</span><span class="nb">len</span><span class="p">)</span> + <span class="mi">44</span> <span class="n">LOAD_CONST</span> <span class="mi">2</span> <span class="p">(</span><span class="s1">&#39;Python&#39;</span><span class="p">)</span> + <span class="mi">46</span> <span class="n">CALL_FUNCTION</span> <span class="mi">1</span> + <span class="mi">48</span> <span class="n">CALL_FUNCTION</span> <span class="mi">1</span> + <span class="mi">50</span> <span class="n">POP_TOP</span> + <span class="mi">52</span> <span class="n">LOAD_CONST</span> <span class="mi">3</span> <span class="p">(</span><span class="kc">None</span><span class="p">)</span> + <span class="mi">54</span> <span class="n">RETURN_VALUE</span> +</code></pre></div> + +<p><span class="caps">BYTECODE</span> trông lạ nhưng không hề&nbsp;khó.</p> +<p>https://docs.python.org/3/library/dis.html +Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>CPython compiler2021-08-28T00:00:00+07:002021-08-28T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-08-28:/compile.html<p><img alt="img" src="https://images.unsplash.com/photo-1520491286939-1680f46efe91?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MzAxNTg0NzA&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Python thường được biết tới như một ngôn ngữ lập trình scripting/interpreted. +Lập trình viên sau khi viết code xong, chỉ cần gõ python tên_file.py để chạy +code dẫn tới một sự hiểu nhầm phổ biến rằng &#8220;python không compile&nbsp;code&#8221;.</p> +<p>Để tránh các tranh cãi không cần …</p><p><img alt="img" src="https://images.unsplash.com/photo-1520491286939-1680f46efe91?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MzAxNTg0NzA&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Python thường được biết tới như một ngôn ngữ lập trình scripting/interpreted. +Lập trình viên sau khi viết code xong, chỉ cần gõ python tên_file.py để chạy +code dẫn tới một sự hiểu nhầm phổ biến rằng &#8220;python không compile&nbsp;code&#8221;.</p> +<p>Để tránh các tranh cãi không cần thiết, ở đây chỉ nói tới CPython - tức bản +Python phổ biến nhất mà gần như tất cả mọi người đều dùng, tải từ +<a href="https://www.python.org/">python.org</a>.</p> +<p>Để chạy 1 file code Python, CPython có bước compile code, nhưng bước này +CPython tự thực hiện mà không cần lập trình viên phải thực hiện. Việc compile +này cũng thường diễn ra rất nhanh chóng nên khó có thể phát hiện ra. Khác với +C/C++/Golang, Python compile code không sinh ra 1 file binary chạy được. Giống +với Java, Python sinh ra bytecode, bytecode này được chạy bởi Python virtual +machine (với Java là <span class="caps">JVM</span>).</p> +<p>Dễ quan sát hơn khi sử dụng module, CPython mặc định sẽ sinh ra file <code>.pyc</code> +chứa compiled bytecode. Tạo 1 file tên <code>mylib.py</code> và 1 file main <code>main.py</code></p> +<div class="highlight"><pre><span></span><code><span class="c1"># main.py</span> +<span class="kn">import</span> <span class="nn">mylib</span> +<span class="nb">print</span><span class="p">(</span><span class="n">mylib</span><span class="o">.</span><span class="n">double</span><span class="p">(</span><span class="mi">21</span><span class="p">))</span> + + <span class="c1"># mylib.py</span> +<span class="k">def</span> <span class="nf">double</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> +</code></pre></div> + +<p>chạy file&nbsp;main.py</p> +<div class="highlight"><pre><span></span><code>$ ls -la +total <span class="m">16</span> +drwxrwxr-x <span class="m">2</span> hvn hvn <span class="m">4096</span> Aug <span class="m">28</span> <span class="m">20</span>:39 . +drwxrwxr-x <span class="m">3</span> hvn hvn <span class="m">4096</span> Aug <span class="m">28</span> <span class="m">20</span>:39 .. +-rw-rw-r-- <span class="m">1</span> hvn hvn <span class="m">37</span> Aug <span class="m">28</span> <span class="m">20</span>:39 main.py +-rw-rw-r-- <span class="m">1</span> hvn hvn <span class="m">32</span> Aug <span class="m">28</span> <span class="m">20</span>:39 mylib.py +$ python3 main.py +<span class="m">42</span> +$ ls -la +total <span class="m">20</span> +drwxrwxr-x <span class="m">3</span> hvn hvn <span class="m">4096</span> Aug <span class="m">28</span> <span class="m">20</span>:39 . +drwxrwxr-x <span class="m">3</span> hvn hvn <span class="m">4096</span> Aug <span class="m">28</span> <span class="m">20</span>:39 .. +-rw-rw-r-- <span class="m">1</span> hvn hvn <span class="m">37</span> Aug <span class="m">28</span> <span class="m">20</span>:39 main.py +-rw-rw-r-- <span class="m">1</span> hvn hvn <span class="m">32</span> Aug <span class="m">28</span> <span class="m">20</span>:39 mylib.py +drwxrwxr-x <span class="m">2</span> hvn hvn <span class="m">4096</span> Aug <span class="m">28</span> <span class="m">20</span>:39 __pycache__ +$ ls -la __pycache__ +total <span class="m">12</span> +drwxrwxr-x <span class="m">2</span> hvn hvn <span class="m">4096</span> Aug <span class="m">28</span> <span class="m">20</span>:39 . +drwxrwxr-x <span class="m">3</span> hvn hvn <span class="m">4096</span> Aug <span class="m">28</span> <span class="m">20</span>:39 .. +-rw-rw-r-- <span class="m">1</span> hvn hvn <span class="m">235</span> Aug <span class="m">28</span> <span class="m">20</span>:39 mylib.cpython-38.pyc +</code></pre></div> + +<p>Python sẽ đọc các file .pyc này mà bỏ qua bước compile ở lần chạy sau (nếu code + không thay đổi), tiết kiệm được thời gian compile, NHƯNG <span class="caps">SAU</span> ĐÓ <span class="caps">CODE</span> +CHẠY KHÔNG <span class="caps">NHANH</span> HƠN lần&nbsp;trước.</p> +<p>Để xem nội dung bytecode sinh ra từ code, dùng <a href="https://docs.python.org/3/library/dis.html">standard lib <code>dis</code></a></p> +<h3>Tham&nbsp;khảo</h3> +<ul> +<li><a href="https://docs.python.org/3/glossary.html#term-bytecode">bytecode</a></li> +<li><a href="https://docs.python.org/3/glossary.html#term-virtual-machine">virtual-machine</a></li> +<li><a href="https://www.python.org/dev/peps/pep-3147/">pep-3147</a></li> +<li><a href="https://docs.python.org/3/tutorial/modules.html?highlight=compile#compiled-python-files">compiled-python-files</a></li> +</ul> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Tự làm phép cộng2021-08-25T00:00:00+07:002021-08-25T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-08-25:/overloading.html<p><img alt="img" src="https://images.unsplash.com/photo-1624355940421-0078a0714838?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2Mjk4OTc5NTQ&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Các phép toán trong Python thực chất là các &#8220;syntactic sugar&#8221;, giúp cho viết +gọn hơn thay vì dùng các method thực sự bên dưới. Phép cộng, nói 1 cách đơn +giản, sẽ gọi method <code>__add__</code>. Việc này làm thay đổi ý nghĩa thông thường của +dấu + để cộng các …</p><p><img alt="img" src="https://images.unsplash.com/photo-1624355940421-0078a0714838?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2Mjk4OTc5NTQ&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Các phép toán trong Python thực chất là các &#8220;syntactic sugar&#8221;, giúp cho viết +gọn hơn thay vì dùng các method thực sự bên dưới. Phép cộng, nói 1 cách đơn +giản, sẽ gọi method <code>__add__</code>. Việc này làm thay đổi ý nghĩa thông thường của +dấu + để cộng các số, có tên gọi là <a href="https://docs.python.org/3/reference/datamodel.html#special-method-names">&#8220;operator overloading&#8221;</a>. +Trong Python, <a href="https://docs.python.org/3/reference/datamodel.html#special-method-names">&#8220;operator overloading&#8221;</a> được dùng phổ biến, khi string cũng + được với nhau, list cũng + được với&nbsp;nhau.</p> +<p>Ví dụ sau tự định nghĩa class MyInt và thực hiện phép cộng +cho kết quả&#8230; bất&nbsp;ngờ:</p> +<div class="highlight"><pre><span></span><code><span class="k">class</span> <span class="nc">MyInt</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">n</span><span class="p">):</span> + <span class="bp">self</span><span class="o">.</span><span class="n">n</span> <span class="o">=</span> <span class="n">n</span> + <span class="k">def</span> <span class="fm">__add__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">k</span><span class="p">):</span> + <span class="k">return</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">n</span> <span class="o">+</span> <span class="n">k</span><span class="o">.</span><span class="n">n</span><span class="p">)</span> <span class="o">*</span> <span class="mi">2</span> +<span class="n">two</span> <span class="o">=</span> <span class="n">MyInt</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> +<span class="n">three</span> <span class="o">=</span> <span class="n">MyInt</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> +<span class="nb">print</span><span class="p">(</span><span class="n">two</span> <span class="o">+</span> <span class="n">three</span> <span class="o">==</span> <span class="mi">10</span><span class="p">)</span> +</code></pre></div> + +<p><a href="https://glot.io/snippets/g1r0ytsfbq">glot.io</a></p> +<p>Tham khảo phép tính += thực sự làm gì tại <a href="https://pymi.vn/blog/augassign/">https://pymi.vn/blog/augassign/</a>.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>StackOverflow khảo sát thị trường 2021: Python most wanted2021-08-06T00:00:00+07:002021-08-06T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-08-06:/mostwanted.html<p><img alt="img" src="https://images.unsplash.com/photo-1526336179256-1347bdb255ee?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjgyMTM0ODU&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>StackOverflow.com - trang web +không thể thiếu của mọi lập trình viên (để hỏi đáp), đã công bố kết quả khảo sát +thị trường năm 2021 thực hiện từ tháng 5 đến tháng 6 2021. Kết quả đầy điều thú vị +với lập trình viên&nbsp;Python:</p> +<ul> +<li>Python vượt <span class="caps">SQL</span>, chiếm …</li></ul><p><img alt="img" src="https://images.unsplash.com/photo-1526336179256-1347bdb255ee?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjgyMTM0ODU&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>StackOverflow.com - trang web +không thể thiếu của mọi lập trình viên (để hỏi đáp), đã công bố kết quả khảo sát +thị trường năm 2021 thực hiện từ tháng 5 đến tháng 6 2021. Kết quả đầy điều thú vị +với lập trình viên&nbsp;Python:</p> +<ul> +<li>Python vượt <span class="caps">SQL</span>, chiếm top 3 ngôn ngữ lập trình/markup phổ biến nhất <a href="https://insights.stackoverflow.com/survey/2021#section-most-popular-technologies-programming-scripting-and-markup-languages">link</a></li> +<li>Flask vẫn phổ biến hơn Django <a href="https://insights.stackoverflow.com/survey/2021#section-most-popular-technologies-web-frameworks">link</a></li> +<li>FastAPI chiếm vị trí số 3 top framework được yêu thích nhất <a href="https://insights.stackoverflow.com/survey/2021#section-most-loved-dreaded-and-wanted-programming-scripting-and-markup-languages">link</a></li> +<li>Numpy/Pandas vẫn top 2 - top 4 library/framework <a href="https://insights.stackoverflow.com/survey/2021#section-most-popular-technologies-other-frameworks-and-libraries">link</a></li> +<li> +<p>Python tiếp tục giữ vị trí &#8220;most-wanted&#8221; 5 năm liền <a href="https://insights.stackoverflow.com/survey/2021#section-most-loved-dreaded-and-wanted-programming-scripting-and-markup-languages">link</a></p> +</li> +<li> +<p>KHÔNG PHẢI <span class="caps">PYTHON</span>: Clojure là ngôn ngữ được trả lương cao&nbsp;nhất.</p> +</li> +</ul> +<p>Xem đầy đủ tại: <a href="https://insights.stackoverflow.com/survey/2021">https://insights.stackoverflow.com/survey/2021</a></p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>ProjectEuler 162021-08-01T00:00:00+07:002021-08-01T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-08-01:/pe16.html<p><a href="https://projecteuler.net/problem=16"><span class="caps">PE16</span></a>:</p> +<blockquote> +<p>Tính tổng các chữ số của 2 mũ&nbsp;1000.</p> +</blockquote> +<p><img alt="img" src="https://images.unsplash.com/photo-1553152531-b98a2fc8d3bf?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2Mjc4MDI0MjM&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Một bài toán quen thuộc đối với các học viên <a href="https://pymi.vn">pymivn</a>, sau 4 buổi học dễ dàng giải bằng 1 dòng&nbsp;code:</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="nb">sum</span><span class="p">(</span><span class="nb">int</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="nb">str</span><span class="p">(</span><span class="mi">2</span><span class="o">**</span><span class="mi">1000</span><span class="p">))</span> +</code></pre></div> + +<p>đây là ví dụ tuyệt vời để minh họa …</p><p><a href="https://projecteuler.net/problem=16"><span class="caps">PE16</span></a>:</p> +<blockquote> +<p>Tính tổng các chữ số của 2 mũ&nbsp;1000.</p> +</blockquote> +<p><img alt="img" src="https://images.unsplash.com/photo-1553152531-b98a2fc8d3bf?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2Mjc4MDI0MjM&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Một bài toán quen thuộc đối với các học viên <a href="https://pymi.vn">pymivn</a>, sau 4 buổi học dễ dàng giải bằng 1 dòng&nbsp;code:</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="nb">sum</span><span class="p">(</span><span class="nb">int</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="nb">str</span><span class="p">(</span><span class="mi">2</span><span class="o">**</span><span class="mi">1000</span><span class="p">))</span> +</code></pre></div> + +<p>đây là ví dụ tuyệt vời để minh họa sự tiện lợi của&nbsp;Python:</p> +<ul> +<li>dễ dàng đổi giữa các&nbsp;kiểu</li> +<li>list&nbsp;comprehension</li> +<li>tính số lớn tùy&nbsp;ý</li> +</ul> +<p><strong>Thử thách lần này là: nếu giới hạn về kết quả mọi phép tính trong bài không được lớn hơn 4 tỷ, hãy tìm đáp án của bài&nbsp;này?</strong></p> +<p>Tức không được gõ <code>2**1000</code>, hay bất kì phép tính nào xuất hiện trong code có giá trị lớn hơn 4&nbsp;tỷ.</p> +<ul> +<li><span class="caps">PS</span>: Đây là giới hạn kích thước kiểu dữ liệu integer dương (unsigned int) trong C/C++ <code>2**32 == 4294967296</code>.</li> +<li><span class="caps">PPS</span>: C/C++ có kiểu <code>unsigned long long</code> có giá trị lớn nhất là <code>2**64</code>.</li> +</ul> +<p>Hãy tạo <a href="https://github.com/pymivn/news/pulls">pull request</a> với label #pe16 nhé. Đáp án sẽ có ở bài&nbsp;sau.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Kỳ diệu range2021-07-09T00:00:00+07:002021-07-09T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-07-09:/range.html<p>Bất kỳ ai học Python cũng đều biết đến range, function giúp tạo 1 list các +số&nbsp;nguyên:</p> +<div class="highlight"><pre><span></span><code><span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">100</span><span class="p">)</span> +</code></pre></div> + +<p><img alt="img" src="https://images.unsplash.com/photo-1550031675-d8ce0062caf7?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjU3OTU0MzY&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p><code>range</code> có trong Python từ rất lâu rồi, thời Python2, <code>range</code> có kèm 1 người +anh em song sinh tên gọi <code>xrange</code>. <code>xrange</code> này chính là <code>range</code> ở Python3 …</p><p>Bất kỳ ai học Python cũng đều biết đến range, function giúp tạo 1 list các +số&nbsp;nguyên:</p> +<div class="highlight"><pre><span></span><code><span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">100</span><span class="p">)</span> +</code></pre></div> + +<p><img alt="img" src="https://images.unsplash.com/photo-1550031675-d8ce0062caf7?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjU3OTU0MzY&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p><code>range</code> có trong Python từ rất lâu rồi, thời Python2, <code>range</code> có kèm 1 người +anh em song sinh tên gọi <code>xrange</code>. <code>xrange</code> này chính là <code>range</code> ở Python3. +Khác gì&nbsp;nhau?</p> +<ul> +<li>range (cũ) sẽ tạo ra 1&nbsp;list</li> +<li>xrange hay range python3 sẽ tạo ra&#8230; 1&nbsp;range</li> +</ul> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">100</span><span class="p">)</span> +<span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">100</span><span class="p">)</span> +<span class="o">&gt;&gt;&gt;</span> <span class="nb">type</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">100</span><span class="p">))</span> +<span class="o">&lt;</span><span class="k">class</span> <span class="err">&#39;</span><span class="nc">range</span><span class="s1">&#39;&gt;</span> +</code></pre></div> + +<p>Vậy nên trong Python3, khi muốn tạo list, phải gõ <code>list(range(1, 100)</code>. +Tại&nbsp;sao?</p> +<p>Nếu dùng <code>range</code> Python2, sẽ khó mà tạo được list chứa 1 tỷ phần tử (do máy sẽ +hết <span class="caps">RAM</span>), nhưng <code>range</code> Python3 thì tạo cái gì cũng được, và nó chỉ trả +về phần tử khi yêu cầu - đây là ý tưởng chính của <a href="https://pp.pymi.vn/article/tuple_comps/">&#8220;generator&#8221;</a>.</p> +<p>Nhưng range không phải generator, range là kiểu range, vì nó còn đặc biệt hơn. +Một <a href="https://pp.pymi.vn/article/tuple_comps/">generator</a> không có len, range thì&nbsp;có:</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="nb">len</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">1_000_000_000</span><span class="p">,</span> <span class="mi">3</span><span class="p">))</span> +<span class="mi">333333333</span> +<span class="o">&gt;&gt;&gt;</span> <span class="k">def</span> <span class="nf">gen</span><span class="p">():</span> +<span class="o">...</span> <span class="k">yield</span> <span class="mi">1</span> +<span class="o">...</span> +<span class="o">&gt;&gt;&gt;</span> <span class="nb">len</span><span class="p">(</span><span class="n">gen</span><span class="p">())</span> +<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span> + <span class="n">File</span> <span class="s2">&quot;&lt;stdin&gt;&quot;</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o">&lt;</span><span class="n">module</span><span class="o">&gt;</span> +<span class="ne">TypeError</span><span class="p">:</span> <span class="nb">object</span> <span class="n">of</span> <span class="nb">type</span> <span class="s1">&#39;generator&#39;</span> <span class="n">has</span> <span class="n">no</span> <span class="nb">len</span><span class="p">()</span> +</code></pre></div> + +<p>Bonus: đọc code tính len của range - code C: +<a href="https://github.com/python/cpython/blob/main/Objects/rangeobject.c#L182">https://github.com/python/cpython/blob/main/Objects/rangeobject.c#L182</a></p> +<h2>Hết</h2> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Lớp Python PyMivn khóa 37 tại Hà Nội khai giảng 1/7/20212021-06-24T00:00:00+07:002021-06-24T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-06-24:/pymihn2107.html<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để trở thành lập trình viên #python chuyên&nbsp;nghiệp.</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1607188924640-b3382257fee7?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjQ1NDUyODg&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Học viên không ở Hà Nội có thể học qua livestream trên Youtube với kênh chat +Slack cùng các học viên Hà&nbsp;Nội.</p> +<p>Lịch học: <a href="https://gitlab.com/pyfml/prepare/-/boards/2862036">link</a></p> +<p>Chi tiết tại trang chủ <a href="https://pymi.vn">Pymivn</a>. +Thắc mắc/hỏi …</p><p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để trở thành lập trình viên #python chuyên&nbsp;nghiệp.</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1607188924640-b3382257fee7?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjQ1NDUyODg&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Học viên không ở Hà Nội có thể học qua livestream trên Youtube với kênh chat +Slack cùng các học viên Hà&nbsp;Nội.</p> +<p>Lịch học: <a href="https://gitlab.com/pyfml/prepare/-/boards/2862036">link</a></p> +<p>Chi tiết tại trang chủ <a href="https://pymi.vn">Pymivn</a>. +Thắc mắc/hỏi đáp: <a href="https://invite.pymi.vn">slack</a></p>Tôi muốn … em2021-06-19T00:00:00+07:002021-06-19T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-06-19:/ellipsis.html<p><code>...</code> là một keyword hợp lệ trong Python, đã có từ rất lâu, và rất ít được biết&nbsp;tới.</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1560336767-9bb468f204c0?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjQwNzMxNzQ&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p><code>...</code> trong tiếng Anh là Ellipsis, trong Python có thể viết 1 trong 2 giá trị này, chúng có kiểu <code>ellipsis</code> chữ e viết&nbsp;thường.</p> +<div class="highlight"><pre><span></span><code>&gt;&gt;&gt; x = ... +&gt;&gt;&gt; type(x) +&lt;class &#39;ellipsis&#39;&gt; +&gt;&gt;&gt; Ellipsis is ... +True …</code></pre></div><p><code>...</code> là một keyword hợp lệ trong Python, đã có từ rất lâu, và rất ít được biết&nbsp;tới.</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1560336767-9bb468f204c0?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjQwNzMxNzQ&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p><code>...</code> trong tiếng Anh là Ellipsis, trong Python có thể viết 1 trong 2 giá trị này, chúng có kiểu <code>ellipsis</code> chữ e viết&nbsp;thường.</p> +<div class="highlight"><pre><span></span><code>&gt;&gt;&gt; x = ... +&gt;&gt;&gt; type(x) +&lt;class &#39;ellipsis&#39;&gt; +&gt;&gt;&gt; Ellipsis is ... +True +</code></pre></div> + +<p>Ellipsis dùng để giữ chỗ khi cần, ví dụ khi viết&nbsp;function:</p> +<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">pikachu</span><span class="p">():</span> + <span class="o">...</span> +</code></pre></div> + +<p>có tác dụng&nbsp;như</p> +<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">pikachu</span><span class="p">()</span> + <span class="k">pass</span> +</code></pre></div> + +<p>Mới đây, <code>...</code> được trọng dụng trong 1 framework siêu hot mới nổi có tên <a href="https://fastapi.tiangolo.com/tutorial/body-multiple-params/">FastAPI</a>:</p> +<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">update_item</span><span class="p">(</span> + <span class="n">item_id</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">item</span><span class="p">:</span> <span class="n">Item</span><span class="p">,</span> <span class="n">user</span><span class="p">:</span> <span class="n">User</span><span class="p">,</span> <span class="n">importance</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="n">Body</span><span class="p">(</span><span class="o">...</span><span class="p">)</span> +<span class="p">)</span> +</code></pre></div> + +<h3>Ellipsis</h3> +<blockquote> +<p>The same as the ellipsis literal “&#8230;”. Special value used mostly in conjunction with extended slicing syntax for user-defined container data&nbsp;types.</p> +</blockquote> +<h3>Tham&nbsp;khảo</h3> +<ul> +<li><a href="https://docs.python.org/3/library/constants.html#Ellipsis">https://docs.python.org/3/library/constants.html#Ellipsis</a></li> +</ul> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Là và bằng2021-06-18T00:00:00+07:002021-06-18T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-06-18:/equal.html<p>Nếu học Python tại Pymi.vn, chắc chắn bạn biết 1 bí mật ít khi dùng tới: 1 == True và 0 ==&nbsp;False:</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="kc">True</span> <span class="o">==</span> <span class="mi">1</span> <span class="ow">and</span> <span class="kc">False</span> <span class="o">==</span> <span class="mi">0</span> +<span class="kc">True</span> +</code></pre></div> + +<p>Thậm chí có thể đem tính&nbsp;toán:</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="kc">True</span> <span class="o">+</span> <span class="kc">True</span> <span class="o">-</span> <span class="kc">False</span> +<span class="mi">2</span> +</code></pre></div> + +<p>bí mật này dẫn tới một số vấn đề &#8220;kỳ lạ …</p><p>Nếu học Python tại Pymi.vn, chắc chắn bạn biết 1 bí mật ít khi dùng tới: 1 == True và 0 ==&nbsp;False:</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="kc">True</span> <span class="o">==</span> <span class="mi">1</span> <span class="ow">and</span> <span class="kc">False</span> <span class="o">==</span> <span class="mi">0</span> +<span class="kc">True</span> +</code></pre></div> + +<p>Thậm chí có thể đem tính&nbsp;toán:</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="kc">True</span> <span class="o">+</span> <span class="kc">True</span> <span class="o">-</span> <span class="kc">False</span> +<span class="mi">2</span> +</code></pre></div> + +<p>bí mật này dẫn tới một số vấn đề &#8220;kỳ lạ&nbsp;sau&#8221;.</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="n">xs</span> <span class="o">=</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="mi">2</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="o">&gt;&gt;&gt;</span> <span class="n">xs</span><span class="o">.</span><span class="n">index</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> +<span class="mi">0</span> +</code></pre></div> + +<p>sẽ có ít nhất 68% được hỏi trả lời sai câu này, dù trình độ code Python lâu đến mấy. +Lý do vì trong thực tế, gần như không bao giờ gặp trường hợp như vậy, việc tạo 1 list chứa cả boolean lẫn integer đã là code quá dở&nbsp;rồi.</p> +<p>Ngày hôm qua, trên &#8220;room Telegram chat sách&#8221; của Pymi (tham gia <a href="https://invite.pymi.vn">Slack</a> vào #chem-gio để ra nhập), thành viên cốt cán @no7kai có&nbsp;đố:</p> +<div class="highlight"><pre><span></span><code><span class="n">dic</span> <span class="o">=</span> <span class="p">{</span><span class="kc">True</span><span class="p">:</span> <span class="s1">&#39;yes&#39;</span><span class="p">,</span> <span class="mi">1</span><span class="p">:</span> <span class="s1">&#39;no&#39;</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">:</span> <span class="s1">&#39;maybe&#39;</span><span class="p">}</span> +<span class="n">dic</span><span class="p">[</span><span class="kc">True</span><span class="p">]</span> +<span class="n">dic</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> +</code></pre></div> + +<p>no7kai&gt; Nhìn code đọc luôn kq&nbsp;ae.</p> +<p>Ta cheating, mang luôn đi&nbsp;chạy</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="n">dic</span> <span class="o">=</span> <span class="p">{</span><span class="kc">True</span><span class="p">:</span> <span class="s1">&#39;yes&#39;</span><span class="p">,</span> <span class="mi">1</span><span class="p">:</span> <span class="s1">&#39;no&#39;</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">:</span> <span class="s1">&#39;maybe&#39;</span><span class="p">}</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">dic</span> +<span class="p">{</span><span class="kc">True</span><span class="p">:</span> <span class="s1">&#39;maybe&#39;</span><span class="p">}</span> +</code></pre></div> + +<p>có người sẽ thấy ngạc nhiên, nhưng Python có ghi rõ trong doc (tất nhiên cái phần doc này cũng không mấy ai&nbsp;đọc:</p> +<blockquote> +<p>A dictionary’s keys are almost arbitrary values. Values that are not hashable, that is, values containing lists, dictionaries or other mutable types (that are compared by value rather than by object identity) may not be used as keys. Numeric types used for keys obey the normal rules for numeric comparison: if two numbers compare equal (such as 1 and 1.0) then they can be used interchangeably to index the same dictionary entry. (Note however, that since computers store floating-point numbers as approximations it is usually unwise to use them as dictionary&nbsp;keys.)</p> +</blockquote> +<p><a href="https://docs.python.org/3/library/stdtypes.html#mapping-types-dict">mapping-types-dict</a></p> +<p>Khi giá trị bằng nhau, chúng được dùng thay thế&nbsp;nhau</p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="n">dic</span> +<span class="p">{</span><span class="kc">True</span><span class="p">:</span> <span class="s1">&#39;maybe&#39;</span><span class="p">}</span> +<span class="o">&gt;&gt;&gt;</span> <span class="n">dic</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> +<span class="s1">&#39;maybe&#39;</span> +</code></pre></div> + +<p>Ví dụ ban đầu và ví dụ này, Python đều so sánh <code>==</code> để thực hiện các phép&nbsp;tính.</p> +<h3>Hỏi&nbsp;khó</h3> +<p>vậy còn set? set([True, 1]) thì trả về mấy phần&nbsp;tử?</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1542134377-e67fbf4ca699?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjM5ODE1ODM&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<div class="highlight"><pre><span></span><code><span class="o">&gt;&gt;&gt;</span> <span class="nb">set</span><span class="p">([</span><span class="kc">True</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">])</span> +<span class="p">{</span><span class="kc">True</span><span class="p">}</span> +</code></pre></div> + +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Python len trả về kết quả: ngay và luôn2021-06-17T00:00:00+07:002021-06-17T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-06-17:/len.html<p><img alt="img" src="https://images.unsplash.com/photo-1553880376-2dec478c8e3b?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjM4OTI4ODE&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Vì <a href="https://n.pymi.vn/listarray.html">CPython list là 1 array</a>, mà 1 array luôn chứa (phía +sau) kích thước/độ dài +của nó, nên việc tính len(x) luôn luôn trả về kết quả ngay lập tức không phụ +thuộc vào độ lớn của list hay còn gọi là &#8220;có độ phức tạp&nbsp;O …</p><p><img alt="img" src="https://images.unsplash.com/photo-1553880376-2dec478c8e3b?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjM4OTI4ODE&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Vì <a href="https://n.pymi.vn/listarray.html">CPython list là 1 array</a>, mà 1 array luôn chứa (phía +sau) kích thước/độ dài +của nó, nên việc tính len(x) luôn luôn trả về kết quả ngay lập tức không phụ +thuộc vào độ lớn của list hay còn gọi là &#8220;có độ phức tạp&nbsp;O(1)&#8221;.</p> +<p><a href="https://docs.python.org/3/faq/design.html#how-are-lists-implemented-in-cpython">How are lists implemented in&nbsp;CPython?</a></p> +<blockquote> +<p>CPython’s lists are really variable-length arrays, not Lisp-style linked +lists. The implementation uses a contiguous array of references to other +objects, and keeps a pointer to this array and the array’s length in a list +head&nbsp;structure.</p> +</blockquote> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Enum trong Python2021-06-10T00:00:00+07:002021-06-10T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-06-10:/enum.html<p>Khái niệm Enum có trong hầu hết các ngôn ngữ static typing nhưng mãi đến 3.4 +mới xuất hiện trong Python standard lib <a href="https://docs.python.org/3/library/enum.html">enum</a>. +Tức Python developers đã nghĩ ra cách để không phải dùng +enum cũng chẳng sao&#8230; nhưng có thì&nbsp;tốt.</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1575846008827-b0167ab20bb9?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjMyNDkyNTI&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<h2>Enum để làm&nbsp;gì?</h2> +<p>Enum là …</p><p>Khái niệm Enum có trong hầu hết các ngôn ngữ static typing nhưng mãi đến 3.4 +mới xuất hiện trong Python standard lib <a href="https://docs.python.org/3/library/enum.html">enum</a>. +Tức Python developers đã nghĩ ra cách để không phải dùng +enum cũng chẳng sao&#8230; nhưng có thì&nbsp;tốt.</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1575846008827-b0167ab20bb9?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjMyNDkyNTI&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<h2>Enum để làm&nbsp;gì?</h2> +<p>Enum là viết tắt của enumeration (tránh nhầm với function enumerate). +Enum là một bộ hữu hạn các tên ứng với các giá trị cố định. +Ví dụ +- kích thước áo chỉ có trong 1 tập giới hạn các giá trị <span class="caps">XXS</span> <span class="caps">XS</span> S M L <span class="caps">XL</span> <span class="caps">XXL</span> +- điểm số tây chỉ nằm trong tập A B C D E F +- các ngày trong tuần chỉ có từ thứ 2 đến chủ nhật +- các lá bài chỉ có 4 chất dô cơ tép bích :))))) (Diamond Heart Club&nbsp;Spade)</p> +<p>Làm thế nào để biểu diễn 4 chất này lên&nbsp;Python?</p> +<p>Cách 1: dùng string &#8216;D&#8217;, &#8216;H&#8217;, &#8216;C&#8217;,&nbsp;&#8216;S&#8217;</p> +<p>Nhược điểm: không so sánh được nếu ta có luật Spade &lt; Club &lt; Diamond &lt; Heart, +lập trình viên cũng có thể gõ nhầm chữ P và để lại lỗi chỉ phát hiện ra lúc&nbsp;chạy.</p> +<p>Cách 2: đặt tên theo kiểu <span class="caps">CONSTANT</span> gán giá trị số - cách này phổ biến ở các +ngôn ngữ không có&nbsp;Enum.</p> +<div class="highlight"><pre><span></span><code><span class="n">SPADE</span> <span class="o">=</span> <span class="mi">1</span> +<span class="n">CLUB</span> <span class="o">=</span> <span class="mi">2</span> +<span class="n">DIAMOND</span> <span class="o">=</span> <span class="mi">3</span> +<span class="n">HEART</span> <span class="o">=</span> <span class="mi">4</span> +<span class="n">ALL_SUITS</span> <span class="o">=</span> <span class="p">(</span><span class="n">SPADE</span><span class="p">,</span> <span class="n">CLUB</span><span class="p">,</span> <span class="n">DIAMOND</span><span class="p">,</span> <span class="n">HEART</span><span class="p">)</span> +</code></pre></div> + +<p>Nhược điểm: người dùng có thể đưa nhầm số khác vào và vẫn chạy, do bản chất +đây là các số. Không lấy được tập giá trị, hoặc phải tự tạo 1&nbsp;list.</p> +<p>Và Enum đến giải cứu, để tạo 1 enum, kế thừa nó từ class Enum, ví dụ game 1&nbsp;cây:</p> +<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">random</span> +<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span><span class="p">,</span> <span class="n">Tuple</span> +<span class="kn">from</span> <span class="nn">enum</span> <span class="kn">import</span> <span class="n">IntEnum</span><span class="p">,</span> <span class="n">auto</span> + + +<span class="k">class</span> <span class="nc">Suit</span><span class="p">(</span><span class="n">IntEnum</span><span class="p">):</span> + <span class="n">SPADE</span> <span class="o">=</span> <span class="n">auto</span><span class="p">()</span> + <span class="n">CLUB</span> <span class="o">=</span> <span class="n">auto</span><span class="p">()</span> + <span class="n">DIAMOND</span> <span class="o">=</span> <span class="n">auto</span><span class="p">()</span> + <span class="n">HEART</span> <span class="o">=</span> <span class="n">auto</span><span class="p">()</span> + + +<span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">Suit</span><span class="p">:</span> + <span class="nb">print</span><span class="p">(</span><span class="n">s</span><span class="p">,</span> <span class="n">s</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="n">s</span><span class="o">.</span><span class="n">value</span><span class="p">)</span> + +<span class="k">assert</span> <span class="n">Suit</span><span class="o">.</span><span class="n">SPADE</span> <span class="o">&lt;</span> <span class="n">Suit</span><span class="o">.</span><span class="n">HEART</span> + + +<span class="k">def</span> <span class="nf">create_cards</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="n">Tuple</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="n">Suit</span><span class="p">]]:</span> + <span class="k">return</span> <span class="p">[(</span><span class="n">i</span><span class="p">,</span> <span class="n">s</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="mi">13</span><span class="p">)</span> <span class="k">for</span> <span class="n">s</span> <span class="ow">in</span> <span class="n">Suit</span><span class="p">]</span> + + +<span class="k">def</span> <span class="nf">play_1_cay</span><span class="p">(</span><span class="n">n</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">s</span><span class="p">:</span> <span class="n">Suit</span><span class="p">):</span> + <span class="n">r</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">create_cards</span><span class="p">())</span> + <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;LeMe: </span><span class="si">{}</span><span class="s2"> vs No: </span><span class="si">{}</span><span class="s2">: </span><span class="si">{}</span><span class="s2">&quot;</span><span class="o">.</span><span class="n">format</span><span class="p">((</span><span class="n">n</span><span class="p">,</span> <span class="n">s</span><span class="p">),</span> <span class="n">r</span><span class="p">,</span> <span class="p">(</span><span class="n">n</span><span class="p">,</span> <span class="n">s</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="n">r</span><span class="p">))</span> + + +<span class="n">play_1_cay</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="n">Suit</span><span class="o">.</span><span class="n">CLUB</span><span class="p">)</span> +<span class="n">play_1_cay</span><span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span> +</code></pre></div> + +<p><a href="https://glot.io/snippets/fze7cclp5r">Link&nbsp;glot.io</a></p> +<p>Output:</p> +<div class="highlight"><pre><span></span><code><span class="n">Suit</span><span class="o">.</span><span class="n">SPADE</span> <span class="n">SPADE</span> <span class="mi">1</span> +<span class="n">Suit</span><span class="o">.</span><span class="n">CLUB</span> <span class="n">CLUB</span> <span class="mi">2</span> +<span class="n">Suit</span><span class="o">.</span><span class="n">DIAMOND</span> <span class="n">DIAMOND</span> <span class="mi">3</span> +<span class="n">Suit</span><span class="o">.</span><span class="n">HEART</span> <span class="n">HEART</span> <span class="mi">4</span> +<span class="n">LeMe</span><span class="p">:</span> <span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="o">&lt;</span><span class="n">Suit</span><span class="o">.</span><span class="n">CLUB</span><span class="p">:</span> <span class="mi">2</span><span class="o">&gt;</span><span class="p">)</span> <span class="n">vs</span> <span class="n">No</span><span class="p">:</span> <span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="o">&lt;</span><span class="n">Suit</span><span class="o">.</span><span class="n">DIAMOND</span><span class="p">:</span> <span class="mi">3</span><span class="o">&gt;</span><span class="p">):</span> <span class="kc">True</span> +<span class="n">LeMe</span><span class="p">:</span> <span class="p">(</span><span class="mi">8</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span> <span class="n">vs</span> <span class="n">No</span><span class="p">:</span> <span class="p">(</span><span class="mi">12</span><span class="p">,</span> <span class="o">&lt;</span><span class="n">Suit</span><span class="o">.</span><span class="n">HEART</span><span class="p">:</span> <span class="mi">4</span><span class="o">&gt;</span><span class="p">):</span> <span class="kc">False</span> +</code></pre></div> + +<p><a href="https://pp.pymi.vn/article/mypy/">mypy</a> sẽ báo: ở dòng gọi <code>play_1_cay(8, 5)</code></p> +<div class="highlight"><pre><span></span><code><span class="n">enumtest</span><span class="o">.</span><span class="n">py</span><span class="p">:</span><span class="mi">24</span><span class="p">:</span><span class="w"> </span><span class="n">error</span><span class="p">:</span><span class="w"> </span><span class="n">Argument</span><span class="w"> </span><span class="mi">2</span><span class="w"> </span><span class="n">to</span><span class="w"> </span><span class="s2">&quot;play_1_cay&quot;</span><span class="w"> </span><span class="n">has</span><span class="w"> </span><span class="n">incompatible</span><span class="w"> </span><span class="n">type</span><span class="w"> </span><span class="s2">&quot;int&quot;</span><span class="p">;</span><span class="w"> </span><span class="n">expected</span><span class="w"> </span><span class="s2">&quot;Suit&quot;</span><span class="w"></span> +</code></pre></div> + +<p><code>auto()</code> tự sinh số tăng&nbsp;dần.</p> +<p>Happy&nbsp;enum.</p> +<h2>Tham&nbsp;khảo</h2> +<ul> +<li>https://docs.python.org/3/library/enum.html</li> +</ul> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Lập trình viên Python có mức lương cao nhất tại Việt Nam 20212021-06-09T00:00:00+07:002021-06-09T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-06-09:/topmoney.html<p>Theo <a href="https://topdev.vn/page/bao-cao-it-viet-nam">Báo Cáo Thị Trường <span class="caps">IT</span> Việt Nam 2021 – Developers Recruitment State</a>, +lập trình viên dùng &#8220;công nghệ&#8221; Python có mức lương cao nhất, ngôn ngữ theo sát +sau là <a href="https://pp.pymi.vn/article/go/">Golang</a>.</p> +<p><img alt="topmoney" src="https://n.pymi.vn/images/topmoney.webp"></p> +<p><em>MỨC LƯƠNG CỦA LẬP TRÌNH VIÊN <span class="caps">THEO</span> CÔNG NGHỆ +(một cách tương đối dành cho đối tượng có tới …</em></p><p>Theo <a href="https://topdev.vn/page/bao-cao-it-viet-nam">Báo Cáo Thị Trường <span class="caps">IT</span> Việt Nam 2021 – Developers Recruitment State</a>, +lập trình viên dùng &#8220;công nghệ&#8221; Python có mức lương cao nhất, ngôn ngữ theo sát +sau là <a href="https://pp.pymi.vn/article/go/">Golang</a>.</p> +<p><img alt="topmoney" src="https://n.pymi.vn/images/topmoney.webp"></p> +<p><em>MỨC LƯƠNG CỦA LẬP TRÌNH VIÊN <span class="caps">THEO</span> CÔNG NGHỆ +(một cách tương đối dành cho đối tượng có tới 3 năm kinh&nbsp;nghiệm)</em></p> +<p>Ngoài điểm sáng này ra, phần còn lại của bản báo cáo có nhiều &#8220;vấn đề kỹ thuật&#8221;, +như chuyện xếp MongoDB và Redis vào nhóm <span class="caps">SQL</span>, nhóm chung các nghề là &#8220;lập trình +viên&#8221; (thay vì DevOps, SysAdmin, Data Analyst, hay thậm chí Scrum Master! &#8230;). +Kèm theo bất ngờ lớn khi <strong>Falcon</strong> vượt +qua <strong>Flask</strong> đứng vị trí thứ 2 trong các bộ &#8220;khung&#8221; (theo báo cáo) +phổ biến nhất của Python tại Việt Nam, trong khi trên thế giới, Flask là số 1 theo +báo cáo của <a href="https://www.jetbrains.com/lp/python-developers-survey-2020/"><span class="caps">PSF</span> <span class="amp">&amp;</span> JetBrains</a>.</p> +<p>Python no 1 khỏi phải bàn&nbsp;😎</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Đáp án vì sao không tool nào thấy bug2021-06-08T00:00:00+07:002021-06-08T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-06-08:/free.html<p>Trong <a href="https://n.pymi.vn/latebinding.html">bài trước</a>, một ví dụ trông đơn giản +và dễ phát hiện lỗi bằng mắt việc x chưa được định nghĩa nhưng không tool nào +của Python phát hiện&nbsp;ra.</p> +<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">n_pymi_vn</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="nb">int</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">1</span> + <span class="k">return</span> <span class="n">s</span> + + +<span class="n">r</span> <span class="o">=</span> <span class="n">n_pymi_vn</span><span class="p">()</span> +<span class="n">x</span> <span class="o">=</span> <span class="mi">10</span> +<span class="nb">print</span><span class="p">(</span><span class="n">r</span><span class="p">)</span> +</code></pre></div> + +<p><img alt="img" src="https://images.unsplash.com/photo-1593627906979-dc2fdc503e32?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjMxMTkyNTE&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Lý do là bởi khái …</p><p>Trong <a href="https://n.pymi.vn/latebinding.html">bài trước</a>, một ví dụ trông đơn giản +và dễ phát hiện lỗi bằng mắt việc x chưa được định nghĩa nhưng không tool nào +của Python phát hiện&nbsp;ra.</p> +<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">n_pymi_vn</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="nb">int</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">1</span> + <span class="k">return</span> <span class="n">s</span> + + +<span class="n">r</span> <span class="o">=</span> <span class="n">n_pymi_vn</span><span class="p">()</span> +<span class="n">x</span> <span class="o">=</span> <span class="mi">10</span> +<span class="nb">print</span><span class="p">(</span><span class="n">r</span><span class="p">)</span> +</code></pre></div> + +<p><img alt="img" src="https://images.unsplash.com/photo-1593627906979-dc2fdc503e32?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjMxMTkyNTE&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Lý do là bởi khái niệm ít được nghe tới <a href="https://pp.pymi.vn/article/free/">&#8220;free variable&#8221;</a>.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Vì sao không chương trình nào phát hiện ra lỗi này?2021-06-06T00:00:00+07:002021-06-06T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-06-06:/latebinding.html<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">n_pymi_vn</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="nb">int</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">1</span> + <span class="k">return</span> <span class="n">s</span> + + +<span class="n">r</span> <span class="o">=</span> <span class="n">n_pymi_vn</span><span class="p">()</span> +<span class="n">x</span> <span class="o">=</span> <span class="mi">10</span> +<span class="nb">print</span><span class="p">(</span><span class="n">r</span><span class="p">)</span> +</code></pre></div> + +<p>Đoạn code này có bug gì? tại sao các tool không phát hiện ra? +và nên sửa thế nào để các tool có thể phát hiện&nbsp;ra?</p> +<p>Xóa 3 dòng cuối đi, flake8 hay mypy sẽ …</p><div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">n_pymi_vn</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="nb">int</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">1</span> + <span class="k">return</span> <span class="n">s</span> + + +<span class="n">r</span> <span class="o">=</span> <span class="n">n_pymi_vn</span><span class="p">()</span> +<span class="n">x</span> <span class="o">=</span> <span class="mi">10</span> +<span class="nb">print</span><span class="p">(</span><span class="n">r</span><span class="p">)</span> +</code></pre></div> + +<p>Đoạn code này có bug gì? tại sao các tool không phát hiện ra? +và nên sửa thế nào để các tool có thể phát hiện&nbsp;ra?</p> +<p>Xóa 3 dòng cuối đi, flake8 hay mypy sẽ phát hiện ra&nbsp;bug:</p> +<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">n_pymi_vn</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="nb">int</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">1</span> + <span class="k">return</span> <span class="n">s</span> + +<span class="err">$</span> <span class="n">flake8</span> <span class="n">main</span><span class="o">.</span><span class="n">py</span> +<span class="n">main</span><span class="o">.</span><span class="n">py</span><span class="p">:</span><span class="mi">2</span><span class="p">:</span><span class="mi">9</span><span class="p">:</span> <span class="n">F821</span> <span class="n">undefined</span> <span class="n">name</span> <span class="s1">&#39;x&#39;</span> +<span class="err">$</span> <span class="n">mypy</span> <span class="n">main</span><span class="o">.</span><span class="n">py</span> +<span class="n">main</span><span class="o">.</span><span class="n">py</span><span class="p">:</span><span class="mi">2</span><span class="p">:</span> <span class="n">error</span><span class="p">:</span> <span class="n">Name</span> <span class="s1">&#39;x&#39;</span> <span class="ow">is</span> <span class="ow">not</span> <span class="n">defined</span> +<span class="n">Found</span> <span class="mi">1</span> <span class="n">error</span> <span class="ow">in</span> <span class="mi">1</span> <span class="n">file</span> <span class="p">(</span><span class="n">checked</span> <span class="mi">1</span> <span class="n">source</span> <span class="n">file</span><span class="p">)</span> +</code></pre></div> + +<p>Chú ý câu hỏi về việc phát hiện ra bug này <strong>trước</strong> khi chạy&nbsp;code.</p> +<p>Việc định nghĩa x = 10 sau <code>def n_pymi_vn</code> hoàn toàn không phải bug, đoạn code +sau chạy <span class="caps">OK</span>, không có&nbsp;bug:</p> +<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">n_pymi_vn</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="nb">int</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">1</span> + <span class="k">return</span> <span class="n">s</span> + + +<span class="n">x</span> <span class="o">=</span> <span class="mi">10</span> +<span class="n">r</span> <span class="o">=</span> <span class="n">n_pymi_vn</span><span class="p">()</span> +<span class="nb">print</span><span class="p">(</span><span class="n">r</span><span class="p">)</span> +</code></pre></div> + +<p>https://glot.io/snippets/fzafpx0nqu</p> +<p><img alt="img" src="https://images.unsplash.com/photo-1571030905044-7458c2a7ac24?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjI5NTM3NTI&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Câu trả lời sẽ được tiết lộ vào bài viết&nbsp;sau.</p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Thư viện pathlib trong Python3.42021-05-27T00:00:00+07:002021-05-27T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-05-27:/pathlib.html<p><img alt="img" src="https://images.unsplash.com/photo-1584802142766-52eda937f127?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjIxMzAzMDQ&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Làm thế nào để viết code mở 1 file ở ngay cùng thư mục file code hiện&nbsp;tại?</p> +<div class="highlight"><pre><span></span><code><span class="nb">open</span><span class="p">(</span><span class="s2">&quot;./data.csv&quot;</span><span class="p">)</span> +</code></pre></div> + +<p>cách này sẽ mở file cùng thư mục làm việc hiện tại (current work directory - +cwd), mặc định là thư mục người dùng gõ lệnh để chạy Python script …</p><p><img alt="img" src="https://images.unsplash.com/photo-1584802142766-52eda937f127?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjIxMzAzMDQ&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Làm thế nào để viết code mở 1 file ở ngay cùng thư mục file code hiện&nbsp;tại?</p> +<div class="highlight"><pre><span></span><code><span class="nb">open</span><span class="p">(</span><span class="s2">&quot;./data.csv&quot;</span><span class="p">)</span> +</code></pre></div> + +<p>cách này sẽ mở file cùng thư mục làm việc hiện tại (current work directory - +cwd), mặc định là thư mục người dùng gõ lệnh để chạy Python script. +Nếu&nbsp;gõ:</p> +<div class="highlight"><pre><span></span><code><span class="n">python</span> <span class="n">script</span><span class="o">.</span><span class="n">py</span> +</code></pre></div> + +<p>thì ok, nhưng nếu đang ở <code>/</code> gõ <code>python /path/to/script.py</code> sẽ fail. +Để lấy cwd, gõ <code>os.getcwd()</code>.</p> +<p>Python có một biến đặc biệt <code>__file__</code> là đường dẫn của chính file code hiện +tại, cách làm truyền thống ở Python2.x sẽ&nbsp;là</p> +<div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">os</span> + +<span class="n">f</span> <span class="o">=</span> <span class="nb">open</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">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">dirname</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">abspath</span><span class="p">(</span> + <span class="vm">__file__</span><span class="p">)),</span> + <span class="s2">&quot;data.csv&quot;</span> + <span class="p">)</span> +<span class="p">)</span> +<span class="nb">print</span><span class="p">(</span><span class="n">f</span><span class="o">.</span><span class="n">read</span><span class="p">())</span> +</code></pre></div> + +<p>bước <code>abspath</code> là cần thiết, vì <code>__path__</code> <a href="https://stackoverflow.com/questions/7116889/is-module-file-attribute-absolute-or-relative">có thể trả về đường dẫn tương đối +(như <code>./script.py</code>).</a></p> +<div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">pathlib</span> <span class="kn">import</span> <span class="n">Path</span> +<span class="n">f</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="vm">__file__</span><span class="p">)</span><span class="o">.</span><span class="n">parent</span><span class="o">.</span><span class="n">absolute</span><span class="p">()</span> <span class="o">/</span> <span class="s2">&quot;data.csv&quot;</span> +<span class="p">)</span> +<span class="nb">print</span><span class="p">(</span><span class="n">f</span><span class="o">.</span><span class="n">read</span><span class="p">())</span> +</code></pre></div> + +<p>ngắn gọn hơn nhiều, với dấu <code>/</code> là dấu phân cách quen thuộc trên các hệ điều +hành không phải&nbsp;Windows.</p> +<p><span class="caps">PS</span>: nếu bạn nghĩ vậy là phức tạp? đây là <a href="https://stackoverflow.com/questions/59895/how-can-i-get-the-source-directory-of-a-bash-script-from-within-the-script-itsel">code&nbsp;bash</a></p> +<h3>Tham&nbsp;khảo</h3> +<ul> +<li>https://docs.python.org/3/library/pathlib.html</li> +<li>https://stackoverflow.com/questions/7116889/is-module-file-attribute-absolute-or-relative</li> +</ul> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>CPython list thực chất là 1 array2021-05-24T00:00:00+07:002021-05-24T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-05-24:/listarray.html<p><img alt="img" src="https://images.unsplash.com/photo-1586294839852-650d52bb6923?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjE4MjAwMTc&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Trong CPython (bản/implementation Python phổ biến nhất - tải tại python.org), +kiểu dữ liệu list thực chất là 1 array tương tự như array trong các ngôn ngữ +lập trình khác (C/Java&#8230;). +Nó không phải kiểu linked-list mà trong ngành khoa học máy tính thường hay +gọi là …</p><p><img alt="img" src="https://images.unsplash.com/photo-1586294839852-650d52bb6923?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjE4MjAwMTc&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Trong CPython (bản/implementation Python phổ biến nhất - tải tại python.org), +kiểu dữ liệu list thực chất là 1 array tương tự như array trong các ngôn ngữ +lập trình khác (C/Java&#8230;). +Nó không phải kiểu linked-list mà trong ngành khoa học máy tính thường hay +gọi là (linkedlist) list (xem link phần tham&nbsp;khảo).</p> +<p>Thư viện standard <code>deque</code> cung cấp kiểu dữ liệu <code>double-ended queue</code>, giống +với kiểu <code>double linked list</code>. Và thư viện <code>array</code> cung cấp kiểu dữ liệu&#8230; +array (như list với yêu cầu các phàn tử phải cùng&nbsp;kiểu).</p> +<p><code>deque</code> có thể dùng để lấy 10 dòng cuối của 1 file rất đơn giản, dùng ít +bộ&nbsp;nhớ:</p> +<div class="highlight"><pre><span></span><code><span class="k">def</span> <span class="nf">tail</span><span class="p">(</span><span class="n">filename</span><span class="p">,</span> <span class="n">n</span><span class="o">=</span><span class="mi">10</span><span class="p">):</span> + <span class="s1">&#39;Return the last n lines of a file&#39;</span> + <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">filename</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span> + <span class="k">return</span> <span class="n">deque</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">n</span><span class="p">)</span> +</code></pre></div> + +<h3>Tham&nbsp;khảo</h3> +<ul> +<li>https://docs.python.org/3/faq/design.html#how-are-lists-implemented-in-cpython</li> +<li>https://docs.python.org/3/library/collections.html#collections.deque</li> +<li>https://docs.python.org/3/library/array.html</li> +<li><a href="https://www.geeksforgeeks.org/linked-list-vs-array/">https://www.geeksforgeeks.org/linked-list-vs-array/</a></li> +</ul> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Từ Python3.7 trở đi, các key trong dict có thứ tự2021-05-21T00:00:00+07:002021-05-21T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-05-21:/dictorder.html<p><img alt="img" src="https://images.unsplash.com/photo-1577303600246-c694ab6ad8a1?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjE1NjIwMjc&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Các key sẽ theo thứ tự chúng được thêm vào&nbsp;dict.</p> +<div class="highlight"><pre><span></span><code><span class="n">In</span> <span class="p">[</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="p">{</span><span class="mi">1</span><span class="p">:</span><span class="kc">None</span><span class="p">,</span> <span class="mi">3</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span> <span class="mi">20</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span> <span class="mi">2</span><span class="p">:</span> <span class="kc">None</span><span class="p">}:</span> + <span class="o">...</span><span class="p">:</span> <span class="nb">print</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> + <span class="o">...</span><span class="p">:</span> +<span class="mi">1</span> +<span class="mi">3</span> +<span class="mi">20</span> +<span class="mi">2</span> +</code></pre></div> + +<p>khác với trước kia, các key không có thứ tự (khác với ngẫu nhiên - ngẫu nhiên +là mỗi lần chạy …</p><p><img alt="img" src="https://images.unsplash.com/photo-1577303600246-c694ab6ad8a1?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjE1NjIwMjc&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Các key sẽ theo thứ tự chúng được thêm vào&nbsp;dict.</p> +<div class="highlight"><pre><span></span><code><span class="n">In</span> <span class="p">[</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="p">{</span><span class="mi">1</span><span class="p">:</span><span class="kc">None</span><span class="p">,</span> <span class="mi">3</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span> <span class="mi">20</span><span class="p">:</span> <span class="kc">None</span><span class="p">,</span> <span class="mi">2</span><span class="p">:</span> <span class="kc">None</span><span class="p">}:</span> + <span class="o">...</span><span class="p">:</span> <span class="nb">print</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> + <span class="o">...</span><span class="p">:</span> +<span class="mi">1</span> +<span class="mi">3</span> +<span class="mi">20</span> +<span class="mi">2</span> +</code></pre></div> + +<p>khác với trước kia, các key không có thứ tự (khác với ngẫu nhiên - ngẫu nhiên +là mỗi lần chạy qua key dict ra 1 kết quả khác nhau, còn ở đây là luôn +giống nhau trong 1 lần chạy&nbsp;code).</p> +<div class="highlight"><pre><span></span><code><span class="err">$</span> <span class="n">python2</span> +<span class="n">Python</span> <span class="mf">2.7.18</span> <span class="p">(</span><span class="n">default</span><span class="p">,</span> <span class="n">Mar</span> <span class="mi">8</span> <span class="mi">2021</span><span class="p">,</span> <span class="mi">13</span><span class="p">:</span><span class="mi">02</span><span class="p">:</span><span class="mi">45</span><span class="p">)</span> +<span class="p">[</span><span class="n">GCC</span> <span class="mf">9.3.0</span><span class="p">]</span> <span class="n">on</span> <span class="n">linux2</span> +<span class="n">Type</span> <span class="s2">&quot;help&quot;</span><span class="p">,</span> <span class="s2">&quot;copyright&quot;</span><span class="p">,</span> <span class="s2">&quot;credits&quot;</span> <span class="ow">or</span> <span class="s2">&quot;license&quot;</span> <span class="k">for</span> <span class="n">more</span> <span class="n">information</span><span class="o">.</span> +<span class="o">&gt;&gt;&gt;</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="p">{</span><span class="mi">1</span><span class="p">:</span><span class="kc">None</span><span class="p">,</span> <span class="mi">3</span><span class="p">:</span><span class="kc">None</span><span class="p">,</span> <span class="mi">20</span><span class="p">:</span><span class="kc">None</span><span class="p">,</span> <span class="mi">2</span><span class="p">:</span><span class="kc">None</span><span class="p">}:</span> +<span class="o">...</span> <span class="nb">print</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="mi">2</span> +<span class="mi">3</span> +<span class="mi">20</span> +<span class="o">&gt;&gt;&gt;</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="p">{</span><span class="mi">1</span><span class="p">:</span><span class="kc">None</span><span class="p">,</span> <span class="mi">3</span><span class="p">:</span><span class="kc">None</span><span class="p">,</span> <span class="mi">20</span><span class="p">:</span><span class="kc">None</span><span class="p">,</span> <span class="mi">2</span><span class="p">:</span><span class="kc">None</span><span class="p">}:</span> +<span class="o">...</span> <span class="nb">print</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="mi">2</span> +<span class="mi">3</span> +<span class="mi">20</span> +</code></pre></div> + +<h3>Tham&nbsp;khảo</h3> +<ul> +<li>https://docs.python.org/3/whatsnew/3.7.html</li> +<li>https://mail.python.org/pipermail/python-dev/2017-December/151283.html</li> +</ul> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Python 3.6 sẽ hết thời vào tháng 12 20212021-05-20T00:00:00+07:002021-05-20T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-05-20:/py36eol.html<p><img alt="img" src="https://images.unsplash.com/photo-1556575157-75a0d60e4835?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjE1MzE1MTk&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Có thể bạn đã biết, bản #python 3 ngon lành đầu tiên được dùng siêu rộng rãi là +3.6 sẽ hết hạn (<span class="caps">EOL</span>) vào cuối năm nay. Đã đến lúc học dần các tính năng mới của +3.8 và upgrade dần đi thôi&nbsp;😜</p> +<p><a href="https://devguide.python.org/#status-of-python-branches">https://devguide.python.org/#status-of-python-branches …</a></p><p><img alt="img" src="https://images.unsplash.com/photo-1556575157-75a0d60e4835?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=MnwyMzI1MzN8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjE1MzE1MTk&amp;ixlib=rb-1.2.1&amp;q=80&amp;w=600"></p> +<p>Có thể bạn đã biết, bản #python 3 ngon lành đầu tiên được dùng siêu rộng rãi là +3.6 sẽ hết hạn (<span class="caps">EOL</span>) vào cuối năm nay. Đã đến lúc học dần các tính năng mới của +3.8 và upgrade dần đi thôi&nbsp;😜</p> +<p><a href="https://devguide.python.org/#status-of-python-branches">https://devguide.python.org/#status-of-python-branches</a></p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để học Python tại Hà Nội <span class="caps">TP</span> <span class="caps">HCM</span> (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa&nbsp;học.</p>Code Advent of code 2020 của giám đốc nghiên cứu Google - Peter Norvig2021-01-09T00:00:00+07:002021-01-09T00:00:00+07:00Pymier0tag:n.pymi.vn,2021-01-09:/aoc2020-norvig.html<p>Lý do bạn quá bận để tham gia &#8220;adventofcode&nbsp;2020&#8221;?</p> +<p>Hãy nghĩ lại, khi giám đốc nghiên cứu của Google - Peter Norvig đã xử đẹp 47/50 bài với 1 file jupyter notebook, với code siêu đẹp, siêu ngắn gọn. Và như thường lệ, đây là &#8220;văn mẫu&#8221; của các …</p><p>Lý do bạn quá bận để tham gia &#8220;adventofcode&nbsp;2020&#8221;?</p> +<p>Hãy nghĩ lại, khi giám đốc nghiên cứu của Google - Peter Norvig đã xử đẹp 47/50 bài với 1 file jupyter notebook, với code siêu đẹp, siêu ngắn gọn. Và như thường lệ, đây là &#8220;văn mẫu&#8221; của các pythonista&nbsp;😍</p> +<p><a href="https://github.com/norvig/pytudes/blob/master/ipynb/Advent-2020.ipynb">https://github.com/norvig/pytudes/blob/master/ipynb/Advent-2020.ipynb</a></p> +<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để trở thành lập trình viên #python chuyên&nbsp;nghiệp.</p> +<p>Lớp <span class="caps">HCM</span> khai giảng tối thứ 3 ngày&nbsp;12/1/2021.</p> +<h1>pymi #pymivn #pyfml&nbsp;#adventofcode</h1>Lớp Python PyMivn khóa 35 tại Sài Gòn - tp Hồ Chí Minh khai giảng 12/1/20212021-01-08T21:00:00+07:002021-01-08T21:00:00+07:00Pymier0tag:n.pymi.vn,2021-01-08:/pymihcm2101.html<p>Đăng ký ngay tại <a href="https://pymi.vn">PyMI.vn</a> để trở thành lập trình viên #python chuyên&nbsp;nghiệp.</p> +<p>Lớp <span class="caps">HCM</span> khai giảng tối thứ 3 ngày&nbsp;12/1/2021.</p> +<p>Lịch học: <a href="https://trello.com/b/Ls5ZsxBS/h%E1%BB%8Dc-python-tp-h%E1%BB%93-ch%C3%AD-minh-pymivn-hcm2101-timetable">link</a></p> +<p>Chi tiết tại trang chủ <a href="https://pymi.vn">Pymivn</a>. +Thắc mắc/hỏi đáp: <a href="https://invite.pymi.vn">slack</a></p> \ No newline at end of file diff --git a/float_max.html b/float_max.html new file mode 100644 index 0000000..39d3fe9 --- /dev/null +++ b/float_max.html @@ -0,0 +1,137 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Số float lớn nhất +

+
+

+ by Pymier0 +

+
+
+
+

img

+

Bài trước sau khi thử lấy 2**2048/2, xảy ra exception

+
+

OverflowError: int too large to convert to float

+
+

float không lớn tùy ý như int.

+

Số float lớn nhất? là inf

+
>>> float('inf')
+inf
+
+ +

Số float lớn nhất sau inf?

+
>>> import sys
+>>> sys.float_info.max
+1.7976931348623157e+308
+>>> sys.float_info.max*2
+inf
+
+ +

1.79 * 10**308, một số có 309 chữ số, độ lớn gần bằng 2**1024.

+
>>> len(str(2**1024))
+309
+>>> 2**1024
+179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137216
+>>> 2**1024*1.0
+Traceback (most recent call last):
+  File "<stdin>", line 1, in <module>
+OverflowError: int too large to convert to float
+>>> 2**1024 > sys.float_info.max
+True
+
+ +

Hết.

+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/free.html b/free.html new file mode 100644 index 0000000..060f647 --- /dev/null +++ b/free.html @@ -0,0 +1,118 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Đáp án vì sao không tool nào thấy bug +

+
+

+ by Pymier0 +

+
+
+
+

Trong bài trước, một ví dụ trông đơn giản +và dễ phát hiện lỗi bằng mắt việc x chưa được định nghĩa nhưng không tool nào +của Python phát hiện ra.

+
def n_pymi_vn() -> int:
+    s = x + 1
+    return s
+
+
+r = n_pymi_vn()
+x = 10
+print(r)
+
+ +

img

+

Lý do là bởi khái niệm ít được nghe tới “free variable”.

+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/grayscale.html b/grayscale.html new file mode 100644 index 0000000..5780e0c --- /dev/null +++ b/grayscale.html @@ -0,0 +1,127 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ OpenCV đọc ảnh đen trắng (grayscale) +

+
+

+ by Pymier0 +

+
+
+
+

Ảnh màu trên máy tính thường sử dụng hệ màu red-green-blue (đỏ - xanh lục - xanh lam) viết tắt là RGB. Bộ màu này có khả năng biểu diễn 256 * 256 * 256 = 16777216 (16 triệu màu), mặc định OpenCV đọc ảnh vào ở dạng ảnh màu. Xem shape của ảnh sau khi đọc thường có dạng (dài, rộng, 3).

+

img

+

Ảnh đen trắng (gọi là grayscale), chỉ có 256 màu từ 0 đến 255 (từ đen tới bớt đen tới trắng). Khi đọc vào, sẽ chỉ thấy shape của array (dài, rộng).

+
import cv2 as cv
+
+cim = cv.imread("/home/hvn/Pictures/anhmau.jpeg")  # cv.IMREAD_COLOR
+print(cim.shape)
+# (1068, 600, 3)
+
+gim = cv.imread("/home/hvn/Pictures/anhmau.jpeg", cv.IMREAD_GRAYSCALE)
+print(gim.shape, "To new crop size: ", gim.shape[0]//2, gim.shape[1]//2)
+#(1068, 600) To new crop size:  534 300
+
+cut = gim[:gim.shape[0]//2, :gim.shape[1]//2]
+cv.imwrite("gray.jpg", cut)
+
+ +

Kết quả thu được 1/4 góc trên trái bức ảnh ban đầu, đen trắng:

+

grayscale.

+

Hết.

+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/hgdb-arch.html b/hgdb-arch.html new file mode 100644 index 0000000..774c9b4 --- /dev/null +++ b/hgdb-arch.html @@ -0,0 +1,183 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Thêm câu lệnh python vào gdb để in ra binary architecture +

+
+

+ by Pymier0 +

+
+
+
+

Architecture

+

Mỗi file binary được compile cho một kiến trúc(architecture) CPU cụ thể.

+

Các architecture phổ biến như:

+
    +
  • x86-32 (32 bits)
  • +
  • x86-64 (64 bits)
  • +
  • arm-64 (phổ biến trên các máy điện thoại/máy tính bảng/hay Apple M1 M2…)
  • +
+

Một file binary đã compile sẵn cho x86-64 thì không thể chạy trực tiếp trên arm-64. +Ví dụ khi download phần mềm prometheus thấy có sẵn file binary cho từng architecture trên từng hệ điều hành.

+

Cách đơn giản nhất là gõ lệnh file tenbinary:

+
$ file /usr/bin/top                                                                           [0]
+/usr/bin/top: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=867346cd2ce3350129fc1e6fb923e92380eaf6e9, for GNU/Linux 3.2.0, stripped
+
+ +

thấy x86-64, ELF là format binary trên Linux, ở đây thấy ELF 64-bit.

+

Trong gdb, gõ

+
(gdb) maintenance info sections ?
+Exec file:
+    `/usr/bin/top', file type elf64-x86-64.
+
+ +

Câu lệnh này rất dài và khó nhớ, viết một câu lệnh mới tên là arch in ra elf64-x86-64 sẽ ngắn gọn hơn nhiều.

+

Thêm command arch vào gdb

+

Đoạn code tham khảo từ PEDA, các bước thực hiện là chạy các câu lệnh của gdb +rồi lấy kết quả ra và in ra phần mong muốn. Thêm vào file code /home/hvn/me/hgdb/hgdb.py như bài trước.

+
import tempfile
+class Arch(gdb.Command):
+    def __init__(self):
+        super (Arch, self).__init__("arch", gdb.COMMAND_USER)
+
+
+    def invoke(self, arg, from_tty):
+        tmpfile = tempfile.mktemp()
+        with open(tmpfile, 'w+') as f:
+            gdb.execute("set logging off")
+            gdb.execute("set height 0")
+            gdb.execute(f"set logging file {tmpfile}")
+            gdb.execute("set logging overwrite on")
+            gdb.execute("set logging redirect on")
+            gdb.execute("set logging on")
+            gdb.execute("maintenance info sections ?")
+            gdb.flush()
+            gdb.execute("set logging off")
+            output = f.read()
+            for line in output.splitlines():
+                if 'file type' in line:
+                    print(line.split()[-1].strip('.'))
+                    break
+Arch()
+
+ +

Chạy:

+
$ gdb -q /bin/top
+Reading symbols from /bin/top...
+(No debugging symbols found in /bin/top)
+(gdb) arch
+elf64-x86-64
+
+ +

Hết.

+

Thực hiện trên

+
$ gdb --version
+GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
+Copyright (C) 2020 Free Software Foundation, Inc.
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
+This is free software: you are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law.
+
+ +

Tham khảo

+

https://sourceware.org/gdb/current/onlinedocs/gdb.html/Extending-GDB.html#Extending-GDB

+

Liên quan

+ +

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/hgdb.html b/hgdb.html new file mode 100644 index 0000000..f2a914f --- /dev/null +++ b/hgdb.html @@ -0,0 +1,214 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Python hello world từ debugger gdb +

+
+

+ by Pymier0 +

+
+
+
+

GDB là gì

+

GDB GDB: The GNU Project Debugger là debugger phổ biến bậc nhất thế giới, hỗ +trợ nhiều ngôn ngữ như C, Go, Rust … +Lập trình viên Python không dùng GDB mà dùng pdb với giao diện tương tự gdb, nhưng lập trình viên CPython (core devs) có thể dùng tới gdb vì code CPython - viết bằng C.

+

Cài đặt

+
sudo apt-get install gdb
+
+ +

bug

+

Photo by Krzysztof Niewolny on Unsplash

+

GDB đã có từ rất rất lâu, tuy đa năng, nhưng khá khó dùng, không “đẹp sẵn”. Khi chơi CTF, hay làm “binary exploitation”/reverse engineer, người dùng thường dùng các bản mở rộng tính năng, đẹp sãn màu mè thay gdb nguyên bản như:

+ +

Trích tài liệu của pwndbg

+
+

Many other projects from the past (e.g., gdbinit, PEDA) and present (e.g. GEF) exist to fill some these gaps. Each provides an excellent experience and great features — but they’re difficult to extend (some are unmaintained, and all are a single 100KB, 200KB, or 300KB file (respectively)).

+
+

Điều thú vị ở đây là cả 3 chương trình này đều viết bằng Python.

+

Từ bản 7 trở đi, GDB hỗ trợ “extending” (mở rộng) bằng các ngôn ngữ khác như Python hay guile, để kiểm tra xem bản mình cài có không gõ:

+
$ gdb --configuration
+This GDB was configured as follows:
+   configure --host=x86_64-linux-gnu --target=x86_64-linux-gnu
+             --with-auto-load-dir=$debugdir:$datadir/auto-load
+             --with-auto-load-safe-path=$debugdir:$datadir/auto-load
+             --with-expat
+             --with-gdb-datadir=/usr/share/gdb (relocatable)
+             --with-jit-reader-dir=/usr/lib/gdb (relocatable)
+             --without-libunwind-ia64
+             --with-lzma
+             --with-babeltrace
+             --without-intel-pt
+             --with-mpfr
+             --without-xxhash
+             --with-python=/usr (relocatable)
+             --without-guile
+             --disable-source-highlight
+             --with-separate-debug-dir=/usr/lib/debug (relocatable)
+             --with-system-gdbinit=/etc/gdb/gdbinit
+
+ +

bản mặc định trên Ubuntu 20.04 này có --with-python hỗ trợ Python và --without-guile không hỗ trợ Guile.

+

Bật Python từ gdb

+

gdb -q để bật gdb lên, sau đó gõ pi (viết tắt của python-interactive) để bật Python interpreter lên:

+
$ gdb -q
+(gdb) pi
+>>> sum(i for i in range(1000) if i % 3 == 0 or i % 5 == 0)
+233168
+
+ +

Tự viết Python extension

+

gdb có 1 file “init” tại $HOME/.gdbinit, viết nội dung sau để gdb load code từ file khi bật lên. Ở đây ví dụ code nằm trong /home/hvn/me/hgdb/hgdb.py:

+
source /home/hvn/me/hgdb/hgdb.py
+
+ +

Trong file /home/hvn/me/hgdb/hgdb.py, viết code Python như thường, để tạo 1 command mới trong gdb, viết class kế thừa gdb.Command, chú ý sys và gdb lib được import sẵn:

+
print(sys.executable)
+print(sys.version)
+print("Hello world, from python")
+gdb.write("Hello world by gdb\n")
+
+
+class HelloWorld(gdb.Command):
+    def __init__(self):
+        super (HelloWorld, self).__init__("hello", gdb.COMMAND_USER)
+
+
+    def invoke(self, arg, from_tty):
+        if arg.strip():
+            name = arg.strip()
+        else:
+            name = "World"
+        print(f"Chào, {name}!")
+
+HelloWorld()
+
+ +

Bật gdb lên:

+
$ gdb -q
+/usr/bin/python
+3.8.10 (default, Nov 14 2022, 12:59:47)
+[GCC 9.4.0]
+Hello world, from python
+Hello world by gdb
+(gdb) hello
+Chào, World!
+(gdb) hello Pymier
+Chào, Pymier!
+
+ +

Hết.

+

Thực hiện trên

+
$ gdb --version
+GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
+Copyright (C) 2020 Free Software Foundation, Inc.
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
+This is free software: you are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law.
+
+ +

Tham khảo

+

https://sourceware.org/gdb/current/onlinedocs/gdb.html/Extending-GDB.html#Extending-GDB

+

Liên quan

+ +

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/httpd.html b/httpd.html new file mode 100644 index 0000000..ba5588a --- /dev/null +++ b/httpd.html @@ -0,0 +1,128 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ chạy web server bằng 1 câu lệnh Python +

+
+

+ by Pymier0 +

+
+
+
+

Python có sẵn trong stdlib thư viện http để bật ngay 1 HTTP server/webserver, +khi chạy THẬT, bạn có thể dùng NGINX, nhưng đôi khi cần “test nhanh”, cài và +config NGINX là một chuyện không hề nhanh.

+

img

+

Gõ:

+
$ python3 -m http.server
+Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
+
+ +

Truy cập qua trình duyệt gõ: localhost:8000

+

thậm chí máy khác cùng mạng cũng có thể truy cập. Server này trả về list các +file trong thư mục hiện tại. Có thể tạo 1 file index.html để trả về nội dung +file này khi người dùng truy cập.

+

Đó là sức mạnh thư viện có sẵn của Python, rất tiện và có ở đâu có Python3. +Một nhược điểm là nó có thể hơi chậm so với mong muốn, dùng busybox cũng chạy +được 1 câu lệnh:

+
$ busybox httpd -fp8000
+
+ +

busybox có sẵn trên Ubuntu 20.04, và rất nhiều nơi khác.

+

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/ieee2021.html b/ieee2021.html new file mode 100644 index 0000000..97ee6de --- /dev/null +++ b/ieee2021.html @@ -0,0 +1,114 @@ + + + + Tin tức Python PyMI.vn + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ +

+ +

+
+
+

+ Python làm chủ mọi cuộc chơi ieee 2021 +

+
+

+ by Pymier0 +

+
+
+
+

Bảng xếp hạng của IEEE năm 2021 về mức độ phổ biến, Python lại một lần nữa đứng số 1, +ở mọi tiêu chí sắp xếp.

+

Từ lập trình nhúng, tới lập trình web, tới hệ thống AI cỡ siêu to khổng lồ, +Python xuất hiện ở mọi nơi.

+

img

+

Link

+

Nếu muốn biết ai là số 2 thì bấm vào xem link nhé

+ +

Đăng ký ngay tại PyMI.vn để học Python tại Hà Nội TP HCM (Sài Gòn), +trở thành lập trình viên #python chuyên nghiệp ngay sau khóa học.

+
+ +
+
+ +
+
+
+
+

© Pymiers

+
+
+ + \ No newline at end of file diff --git a/images/2ty.jpg b/images/2ty.jpg new file mode 100644 index 0000000000000000000000000000000000000000..91cfb2bd94c8de328c512e4aed75137b61438dc2 GIT binary patch literal 30267 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 z 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 literal 0 HcmV?d00001 diff --git a/images/aoc22.png b/images/aoc22.png new file mode 100644 index 0000000000000000000000000000000000000000..e21066015cd86bc4344ee1a02341b65f17d69e4e GIT binary patch literal 180711 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≈j`LV&<3Jg=J`DPD@8?|98> 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

)_=-D6 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 N_8s`LcF&QszX42T-2DIm literal 0 HcmV?d00001 diff --git a/images/compound2.png b/images/compound2.png new file mode 100644 index 0000000000000000000000000000000000000000..2b20cc8189b91026df93ea027d656e5995ef55b5 GIT binary patch literal 17052 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 literal 0 HcmV?d00001 diff --git a/images/cuaitlongnhieu.png b/images/cuaitlongnhieu.png new file mode 100644 index 0000000000000000000000000000000000000000..6efcb6e6a2163eb2639025b3462c655dcaeb84a3 GIT binary patch literal 8287 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 W!Q}dQLpt!h-0{Pw4;36l-}+y{{kI?h literal 0 HcmV?d00001 diff --git a/images/distribution_daily.png b/images/distribution_daily.png new file mode 100644 index 0000000000000000000000000000000000000000..a8ef41e84c768c2eb8999c7fe1ca6e6d47f9b859 GIT binary patch literal 28272 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%0DqFM9W(3?OXZLxH9 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 Q5k=He*SlY&YV-Dg07wTMr~m)} literal 0 HcmV?d00001 diff --git a/images/gray.jpg b/images/gray.jpg new file mode 100644 index 0000000000000000000000000000000000000000..162cf434f65a00b5b449582505232caaf40360bf GIT binary patch literal 66289 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 literal 0 HcmV?d00001 diff --git a/images/hn2110.jpg b/images/hn2110.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dac720678764df913568a59f5c048186e5fd64e7 GIT binary patch literal 88825 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 *aO{ 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 literal 0 HcmV?d00001 diff --git a/images/hssv.png b/images/hssv.png new file mode 100644 index 0000000000000000000000000000000000000000..43da7e89c1549f509aee404ebf4f0befc04a9bda GIT binary patch literal 8936 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 HdhtI1+k9F% literal 0 HcmV?d00001 diff --git a/images/topmoney.webp b/images/topmoney.webp new file mode 100644 index 0000000000000000000000000000000000000000..4f7ef9d21f98d1886b67639cfbd0b14ffa6c25f6 GIT binary patch literal 47248 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 COIGXv literal 0 HcmV?d00001 diff --git a/images/total_saoke_amount.png b/images/total_saoke_amount.png new file mode 100644 index 0000000000000000000000000000000000000000..fc34688fcc0d9789bcacb39428c00ffcb2ba8d35 GIT binary patch literal 8322 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