ラベル python の投稿を表示しています。 すべての投稿を表示
ラベル python の投稿を表示しています。 すべての投稿を表示

2019年5月11日

[python3.6]f-strings

しかさんに、Python3.6からはまた別の文字列の書き方ができるようになった、というコメントをいただいていた。
気になっていたのだが、知らないままの方が幸せということもあるかもしれない。
それに、Raspberry Piに載っているpython3は、今のところ3.5.xのようだったから、すぐには知らなくていいかもしれない。


が、検索したら見つけてしまったので、やってみよう。


こちら。

Python 3.6の新機能f-strings(フォーマット文字列リテラル)について https://codom.hatenablog.com/entry/2016/12/27/000000

たぶん、これのことだろう。
インタラクティブなやつで試してみよう。


>>> val1=100
>>> val2="yoshio"
>>> print(f'{val2} is {val1} years old')
yoshio is 100 years old

ほぅ。
文字列も数字もいけるのか。
Javaだと数字も足し算でつなげられたので、pythonでも同じようにやってエラーにしてしまうのだ。
リプレースメントにはあまり心が惹かれなかったのだが、いちいちstr()で囲まなくて良いのはいいですな。

ただ、python3.5では使えないのよねぇ。

>>> print(f'{val2} is {val1} years old')
   File "<stdin>", line 1
     print(f'{val2} is {val1} years old')
                                       ^
SyntaxError: invalid syntax

まあ、Raspberry Piでも3.6をインストールできそうな気はするのだが、がんばる気力が無かった。。。

2019年4月20日

[python3]文字列の%dみたいなやつは{}になった

最近、テストコードをpython3で書いている。
python3だ、とわざわざ言わないと、以前のようにpython2の期限が近づいていることを忘れてしまうかもしれない。
なるべく「python3」と明記するようにしよう。


それはともかく、私がやっていることはそんなに難しいことではない。
JSON-RPCのインターフェースがあって、python3のAPIが準備されているので、それを呼び出すだけだ。


私の近況はともかく、APIを実行したら、何か結果を出したい。
そして今回は、単に標準出力に出せば良い。
となると、print()だ。
python2であれば制御命令みたいな使い方をされていたが、python3では関数っぽい使い方になった。
まあ、C言語を使っている身としては、そちらの方が自然な気がする。


で、今のところ結果を出力するのに、

print('result=' + val)

だったり、

print(('result: val1=' + val1 + ', ' val2=' + str(val2))

みたいな書き方をしている。

問題は無いのだが・・・リプレースメントというか、C言語のprintf()で言うところの%dや%sみたいな代替文字を使って出力手段があったことは覚えている。

今が、それを使うときだ!


https://docs.python.org/ja/3.6/library/string.html#format-string-syntax
print()特有というよりは、文字列操作の一環なのだろう。
str.format()というのがそれっぽい気がするのだが・・・複雑すぎやしないかい?


あまり難しく考えなければ、arg_nameに数字を使って、波括弧の中にarg_nameを書いて、その順番に並べればいいんじゃなかろうか。

01: hello = 'hello'
02: world = 'world'
03: 
04: print('{0} {1}'.format(hello, world))

hello world

おお、動いた。


これはこれでいいとして、python2のときは%とか使ってた気がする。
あれは、python3では使わなくなったのだろうか?

https://docs.python.org/ja/3/library/string.html#format-examples

「従来の%-書式」とか「古い%-書式」とか書いてあるので、python3だけ覚えておきたいなら気にしなくてよいんだろう。


昔は、このリプレースメントみたいな引数の順番を出力側のフォーマットで入れ替えできる意味が分かってなかったのだけど、多言語化でどうしても語順が入れ替わる場合なんかに有用なのだということに気付いてから、真面目に理解すべきだと考えるようになった。

まあ、いつもログ出力とか、ちょっとした計算結果とかでしか使わないから、+で文字列をつなぐだけのことが多いのだけどね。。。

2019年3月29日

[python]2to3だけでは解決できなかった

前回、python2のサポートが終わるらしいことを今さら知った、という記事を書いた。

困ったときの2to3ということで、ツールに掛けた。
出てきたのは、printのところだけだった。
まあ、そんなに難しいコードは書けないからねぇ。


などとのんびり構えていたのだが、動かすとあちこちで実行時エラーが発生した。
ほとんどが、文字の取り扱いに対するものだ。
python3では「b'xxxx'」みたいにきっちり書かないとアクセスできないAPIが出てきたのだ。
出てきたというか、その辺がpython2と違うというか。

そういうところについては、2to3では解決してくれないようだった。
まあ、そういう微妙なところをちゃんと区別したくなったということかもしれんな。


困るのは、実行時エラーになるということだ。
普段、C言語のようなコンパイラを使う言語の人からすると、実行時にしかエラーが出ないというのが結構苦痛なのだった。
たぶん、pythonも静的解析ツールがあるんだろうなぁ・・・と思いつつ、メインの開発言語じゃないので深く追おうとはしない私であった。

2019年3月16日

[python]python2系の終焉

Raspberry Pi向けにコードを書いている。

どこかで「Raspberry Piの標準開発言語(?)はpythonだ」というのを読んだ気がするので、なるべくPythonで書こうとしている。
まあ、よくわからないときはC言語になるんだけどね。


RaspbianOSをインストールしたてのRaspberry Piで"python -V"と打つと、2.7系のバージョンが返ってくる。
ああ、pythonの2と3があるけど、Raspberry Piでは2系を使うのね。
そう思っていた。


pipをインストールしたときだっただろうか。
なんか、2020年でdeprecatedになる、みたいなメッセージが出てきた。
えっ、python2の世界は永遠に続くわけではないの???


https://pythonclock.org/

どうやら、2020年1月でpython2系のサポートが終わるらしい。
pythonってそういう感じなのねー。
メインで使わない言語って、そういうスケジュールとかまったくわかってなかったですわ。


じゃあ、Raspberry Piがpython2なのはどうなのよ!
ん、もしや・・・

$ python3 -V
Python 3.5.3

うう、まさか両方インストールされているとは・・・。

2018年9月2日

[python]pythonの仮想環境

名前はよく聞くし、なんとなく仮想的なものを意味している感じがするのは分かっている。
そんなvirtualenvだが、意識的に使ったことが無い。
使い方の説明でコマンドが書かれていて、そのまま使っている程度だ。


歳を取ってきて覚えるのが面倒になってきているのを実感しているが、それに逆らってみよう。


https://virtualenv.pypa.io/en/stable/

virtualenv is a tool to create isolated Python environments.

isolatedとあるので、分離してくれるのだろう。


pipを使っていたので意識しなかったが、パッケージはどこかにインストールされる。
"site-packages/"というディレクトリらしい。
作っているものによってはバージョンが異なるパッケージを使いたいこともあるだろう。
そういうときの問題を解決できるらしい。


らしい、というのは、私がそういう状況に陥っていないからだ。
まあ、pythonで大したものを作っていないという意味でもあるが。。。


そういえば、最近は提供されるものでpython3を使っていることが多い。
pipがpip3になっているかのように、virtualenvもvirtualenv3なのだろうか?


そもそも、pipはpython2で、pip3はpython3なのだろうか?
python2がインストールされているからpip3になっていて、python3だけだったらpipでよいのだろうか?


https://docs.python.org/ja/3/tutorial/venv.html?highlight=pip#managing-packages-with-pip

ここではpipになっている。
このサイトがそう説明しているなら、そうなのだろう。


https://docs.python.org/ja/3/installing/index.html#installing-index

python3.4からは一緒にインストールされるようになったということは、それ以前はpipを単独でインストールしていることになる。
確かに、apt installした気がする。
Ubuntu16.04からアンインストールしてからpipを使ってみよう。

$ sudo pip install --upgrade pip
The directory '/home/xxx/.cache/pip/http' or its parent directory is not owned by the current user and the cache has been disabled. Please check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
The directory '/home/xxx/.cache/pip' or its parent directory is not owned by the current user and caching wheels has been disabled. check the permissions and owner of that directory. If executing pip with sudo, you may want sudo's -H flag.
Collecting pip
  Downloading https://files.pythonhosted.org/packages/5f/25/e52d3f31441505a5f3af41213346e5b6c221c9e086a166f3703d2ddaf940/pip-18.0-py2.py3-none-any.whl (1.3MB)
    100% |████████████████████████████████| 1.3MB 1.1MB/s 
Installing collected packages: pip
  Found existing installation: pip 9.0.1
    Uninstalling pip-9.0.1:
      Successfully uninstalled pip-9.0.1
Successfully installed pip-18.0

うーん、よくわからん。

$ sudo -H pip install numpy
Requirement already satisfied: numpy in /usr/lib/python3/dist-packages (1.11.0)

これはpython3だろう。


ここで、ようやく--versionオプションに気付いた。

$ pip2 --version
pip 9.0.1 from /usr/local/lib/python2.7/dist-packages (python 2.7)

$ pip3 --version
pip 18.0 from /usr/local/lib/python3.5/dist-packages/pip (python 3.5)

$ pip --version
pip 18.0 from /usr/local/lib/python3.5/dist-packages/pip (python 3.5)

python-pipをアンインストールしてもpip2は使えるらしい。が、pipはpython3のものだ。


https://docs.python.org/ja/3/installing/index.html#installing-index

で、このサイトを見ると、仮想環境を作るのはvenvが標準らしい。
virtualenvはサードパーティの代替え案とのことだ。



https://docs.python.org/ja/3.7/library/venv.html#module-venv

見ながら使ってみよう。
Ubuntu16.04にpython3.5.2がインストールされていたから、3.7にしてみる。

https://launchpad.net/~deadsnakes/+archive/ubuntu/ppa
こういうppaって、何も考えずに信用していいのか悩むな。。

tarでやるのが確実だろうけど、pythonはUbuntuのモジュールも使っているようだから、何か怖い。
pythonのバージョンごと仮想化する方法もあるんだろう。

では、カレントディレクトリに作ってみる。

$ python3 -m venv ./env1
The virtual environment was not created successfully because ensurepip is not
available.  On Debian/Ubuntu systems, you need to install the python3-venv
package using the following command.

    apt-get install python3-venv

You may need to use sudo with that command.  After installing the python3-venv
package, recreate your virtual environment.

Failing command: ['/home/hiroshi/Python/.env1/bin/python3', '-Im', 'ensurepip', '--upgrade', '--default-pip']

えっ、venvは標準じゃなかったの??
釈然としないが、apt installでpython3-venvをインストールしてやり直す。


フォルダができたので、移動。
そこから何をしたらいいのか分からんかったが、bin/activateをたたけば良いようだ。
https://qiita.com/fiftystorm36/items/b2fd47cf32c7694adc2e

そうすると、コマンドプロンプトの先頭に環境名が出てくるようになった。

~/Python/env1$ source bin/activate
(env1) ~/Python/env1$

試しにnumpyをpipでinstallさせてみた。bin/activate前は「既にインストールされています」というようなメッセージだけだったのだが、bin/activate後はちゃんとインストールされた。


あれ、どうやって元に戻ればいいんだ・・・と思ったら、deactivateというコマンドがあるようだ。
これはbin/ ではないし、whichでも見つからなかった。
でも、実行すると元に戻った。


ここまでやって気付いたが、使っているpython3はpython3.5.2のままだった。
どうも、"python3.7"とやらないとそうなるようだ。
そして、python3.7-venvをインストールしないとvenvが作れなかった。

python3.7でvenvを実行すると、その中の実行もpython3.7になるようだ。
バージョンはそれで保てそうだ。

2017年4月7日

[bc][slack]botを作ってみる

朝起きると、hiro99maは一匹の巨大な毒虫に・・・なってはいなかったが、なぜかslackのbotを作ってみたくなっていた。

仕事の連絡手段としてslackを使っているのだが、左上に「Apps」という項目があることに気付いた。
どうやら、プラグインのようなものと連携して、挙動を追加できるらしい。

ほほう!

そういえば、botというやつで何かできた記憶がある、と思い出し「slack bot」で検索しようとしたら、候補に「python」が出てきた。
pythonなら多少は使えるので、せっかくだから何か作ってみよう。

何がよいか・・・。
ああ、最近、仕事に関係しないbitcoinネタを書いているので、それにしてみよう。


PythonでSlackbotを作る(1) – ビットログ

一番上に出てきたので、これを参照した。
さすが一番上だけあって、読んでできた。

第1回で、botを使えるようにするまでできた。
API Tokenの取得などだ。
なお、私はpython2を使った。
素直に書いてあるbotの実装も試しておこう。

第2回を読めば、だいたいやりたいことがわかる。
私の場合は、こちらからBitcoinのTXIDを伝えて、blockexplorer.com(リンク先はtestnet用)みたいなサイトから情報を取ってきて、テキストで吐き出そうと思ったので、このくらいでよいのだ。


作ったのは、こちら。
https://github.com/hirokuma/slackbot_bitcoin_txid

API Tokenは自分で埋めておくれ。
あと、testnet用にしているので、必要に応じて修正しよう。
難しいことはやってないので、pythonがわかる人なら変更もすぐできるんじゃなかろうか。

 

困ったのは、むしろtestnetの挙動だった。
なんか、ちょうど作っている最中、攻撃が行われていたようなのだ。
やーねー、もう。

2017年3月10日

[py2]16進数値と16進数文字列

何度やっても覚えられない、python2での16進数と文字列の変換。

 

16進数値を、16進数文字列に変換
http://hiro99ma.blogspot.com/2017/01/py216str.html

>>> x = 0x0123456789abcdef
>>> format(x, '016x')
'0123456789abcdef'

 

16進数文字列を、16進数値(というか、数字)に変換

>>> y = '0123456789abcdef'
>>> int(y, 16)
81985529216486895L
>>> format(int(y, 16), 'x')
'123456789abcdef'

 

これでしばらくは大丈夫なはずだ。。。

2017年2月26日

[py]pywin32をWindows10 64bitにインストール

久しぶりに、pythonを使おうとした。
TCPで通信するアプリを作ろうとして、Twistedというライブラリが簡単そうだったのだ。

 

Linuxではpipでインストールできたのだが、Windowsではそれだけだとダメで、サンプルソースを動かそうとすると「import win32apiが無い」と言われる。
どうやら、pywin32というライブラリもいるらしい。

pyCharmに出てきたので追加しようとするも、エラー。
よくわからんが、Windows専用だからか。。。

検索すると、このページが出てきた。
pypiwin32 220 : Python Package Index
whl?
どうも、wheelという形式のパッケージで、バイナリなんかも入っているらしい。
うちのWindowsは64bitなので、2つファイルがあるうちのamd64の方をダウンロードした。

> pip install pypiwin32-220-cp36-none-win_amd64.whl
pypiwin32-220-cp36-none-win_amd64.whl is not a supported wheel on this platform.

エラー?
platformと書いてあるので、python.exeが32bitなのかと思い、win32の方も試したが、やはりダメだ。
なぜ・・・。

どうも、ファイル名の「cp36」は、python3.6の意味らしい。
うちはまだpython2.7を使っていたので、ダメなのだろう。
そういうplatformか。。。

1つ前のバージョンだとcp27もあるのだが、今さら古いのをインストールしても仕方ないので、sourceforgeからexeファイルをダウンロードしてインストールさせた。
Python for Windows Extensions - Browse /pywin32/Build 220 at SourceForge.net

 

https://twistedmatrix.com/trac/#pubsubserver
これを動かして、telnetで接続し、telnetクライアント側から文字を入力してEnterを押すと、エコーバックされるから、動いているのだろう。
最初、TeraTermで同じことをやって動かず、Windowsのtelnet.exeにしたら動いたのだが、これはEnterを押したときの設定を「CR」にしていたためだった。
「CR+LF」にすると、同じ動きをした。

2017年1月26日

[py][raspi]なるべくpythonを速く動かしたい

手軽にpythonを高速で動かす方法がないか調べた。
なお、主に速くしたいのは、Raspberry Piでの動作だ。


最初に見つかったのが、PyPy。
PyPy - Welcome to PyPy
かわいいなまえですね。

virtualenvとかいうのを使いそうに書いてあったけど、まだ使ったことが無い。。。
よくわからなかったので、apt-getしたらインストールできたと思う。

ただ、困ったことに、pipコマンドはpythonもpypyも"pip"なのだ。
パスが違いそうだけど、せめて名前は変えてほしかったところだ。
/usr/bin/pipがpythonのpipで、/usr/local/bin/pipはpypyのpipだったんじゃなかろうか。

あ、pypyのpipは、確かこちらを見ながらやったと思う。
pipをPypyから利用できるようにするよ!

「思う」が多くて済まんね。
あれこれやったので、記憶が残っていないのだ。。

 

pythonでpip installしたものも、pypyで動かすならpypyのpipでinstallがいる。
使っているものが標準でよく使われるものだったためか、pip自体はできた。

が、だ。
Raspberry Piを使ったのは拡張ピンヘッダで他の機器を制御するライブラリが既にあったためで、それはCで書いていた。
どうやら、インターフェースだけちょろちょろっと変更して共有ライブラリにすればimportできるらしいので、Python/C APIというやつで呼び出せるように作っていた。
PythonのC言語拡張モジュール作成ガイド — PythonのC言語拡張モジュール作成ガイド
このライブラリがimportできないのだ。

PyPyとしては、拡張モジュールはCFFIというやつでやるのを推奨しているらしい。
Writing extension modules for pypy — PyPy documentation
じゃあ、これで共有ライブラリをdlopen()すればよいか、とやってみようとしたが、dlopen()が失敗する。。。
pythonだと読めるので、pypyの問題だろう。
フルパスで書いても、なんかInitが付く名前のオブジェクトが見つからないとか、そんなエラーが出たので私は考えるのをやめた。


次に試したのは、cython。
なんて読むんだ?

cythonは、pyファイルの拡張子を変更して、数行のsetup.pyを書いてpythonを実行すれば動く。
動いて、そのpythonファイルをCソースに変換し、soファイルを作る。
それをimportして使うようだった。
チュートリアル基礎編 — Cython 0.17.1 documentation

やってみたが、かなり長い時間がかかって、ちゃんとsoファイルができた。
pythonでimportすると、読める。
おお。

が、だ。
私が変換したのはmainのところで、しかもコマンドラインで引数を渡して動かすタイプだった。
そういうものをimportすると、グローバルのところに書いたprintがちょろっと動くだけだ(当たり前)。
だから、cythonで変換するなら関数単位で使えるようなものがよいのだろう。
importするようなモジュールにしておくと、cythonしやすいだろう。

なんとかsys.argvを引き渡す形式の関数にして、pythonからはリスト形式で引数を渡すように書き換えた。
これはこれで動いたのだけど、別の箇所で例外が発生して動かなかった。
code_funcなんとかとか出てきてた気がするが、もう忘れた。

ダメなパターンがあるのか、チュートリアルの知識だけでやろうとしてるからダメなのかは調べてない。
計算だけしているモジュールもあって、そっちは動いているから、なんかあるのかもしれん。


やろうとしている速度向上が、時間関数を使わないとわからない、とかいうレベルではなく、声を出して「いーち、にーい、・・・」などと数えられるレベルなので、こんな小手先の技では無理だと分かってるんだけどね。

まあ、「あと数歩」という高速化を求めている場合には使えるかもしれんので、覚えておいて損はなかろう。

2017年1月25日

[py2]大きい数字を16進数のstrにする

過去にもやったと思うのだが、歳のせいか思い出せないし、探してしまうので、タイトルで分かるようにしよう。

整数型のサイズが、1, 2, 4, 8バイトであればstructを使っていたのだが、3バイト長だったり、32バイト長だったりすると、そうもいかないと思う。

>>> x = 0x0123456789abcdef
>>> x
81985529216486895L
>>> '%016x' % x
'0123456789abcdef'
>>> '{:016x}'.format(x)
'0123456789abcdef'
>>> format(x, '016x')
'0123456789abcdef'

printするときのように%を使ってやっていたのだが、Stackoverflowにformatを使う例が書かれていた。
%を使うのはold styleなのか。。。

2016年12月29日

[py]PythonのソースをAndroidで動かせないものか→今回はあきらめた

pythonでいくつかソースを書いて、テストしている。
ネットから情報を取得して加工するとか、コマンドラインからの入力を加工してネットに上げるとか、その手のソースだ。

今のところ、pythonはWindowsやLinuxでしか動かしていない。
が、Androidくらいなら動かせないだろうか?と思うではないか。
Androidで動くなら、もうちょっとやれることが増えるかもしれない、ということで、環境を調べてみた。

思ったよりも、色よい返事(検索結果)がない。。。
まあ、私もネイティブの言語で動かすのが筋だとは思っているのだが、とりあえず動けばよい、というレベルの場合、多少不具合があったとしても最初に書いたコードがそのまま使えるのがありがたいのだ。

 

出てきたのは、python for androidとQPython。
なんとなく、python for androidを試してみることにした。


python for android

開始記事を見ながらやるものの、なかなか難しい。
難しいというか、これを書いている時点ではまだうまくいっていない。
Getting Started — python-for-android 0.1 documentation

 

まず、Ubuntu on Windowsを起動。
Installing Dependenciesを見て、apt-get installなどを実行。
Arch Linuxの方はやっていないが、これはMac用なのかな?
続けて、pipを使ってcython、virtualenvをインストール。
python-for-androidはgitの方を使った(pipのだとrecipesでエラーが出たので)。

Android SDKとNDKがいるようなので、インストールする。
が、Bash on WindowsはGUIが使えないので、コマンドラインツールだけをダウンロードし、unzipする。
その後は、tools/androidコマンドを使ってSDKをインストールする。
コマンドライン (CUI) から Android SDK をインストールする | blog.tyye.net
APIは19にした。
21以上だと、ビルド時にチェックされて使えないようなのだ。

環境変数も書いてあるように設定して、p4aを実行。
オプションの指定は、こちらをまねした。
kivy/python-for-android: Turn your Python application into an Android APK

$ p4a apk --requirements=python2 –private $HOME/android/app/helloworld --package=com.blogpost.hiro99ma.helloworld --name="helloworld" --version=0.1 --bootstrap=sdl2

よくわからんが、テンプレートでも作ってくれるんじゃないのか?
"planewavessdl2"という名前のサンプルがあるのかと思ったけど、リポジトリを検索しても出てこないし。

これが、長い。。
いろいろとダウンロードしてビルドしてくれている。

 

そして、エラーが出た。
main.pyが無い、と。
Hello world — python-for-android 0.1 documentation
ドキュメント内を検索したら出てきたので、そのファイルを置いた。
が、ここって古いp4aのドキュメントだから、あやしいかも。

dxコマンドを実行しようとして、Javaのバージョンがあわないとかなんとか。
build-toolsは少し古いものを入れたのだが、どうもbuild-toolsフォルダに新しいのが入っているとそちらが使われるようだ。
linux - Remove Android SDK packages from command line - Stack Overflow
アンインストールは、単なる削除でよいらしい。

ビルドし直すと、またエラーが。
今度は、aaptが「Syntax error: "(" unexpected」といってくる。
バージョンの違いで文法が変わったのか?と思ってbuild-toolsをさらに古くしたが、変わらない。

場所は、~/.local/share/python-for-android/dists/unnamed_dist_1あたりで作業しているようなのだけど、フォルダ構成がeclipseでやっていたときの構成に似ている。
cdで移動して、"ant debug"とすると、やはりaaptのエラーで終わった。
tools/ant/build.xmlを見ると、"aapt –> R.java"というコメントがあるので、その関係だとは思うのだが、ちょっと動かしてみたいというだけでここまで苦労するのは作業的に厳しい気がする。
やはり、githubじゃなくてpipのpython-for-androidを使うべきなのだろうか。。。
Bash on Windowsを使っているというのも不安なところだ。

 

2016年現在の「Python を android で使う」 | site-hhs
こちらを読むと、今わたしが見ていたpython-for-androidはkivyというプラットフォーム関係のようだ。
たしかにビルド方法の説明でも「Kivyアプリ」「Webアプリ」「SDL2ベースアプリ」の3種類に分かれていた。
そこら辺がわからんとダメなんかなぁ。

というわけで、今回はあきらめた。
Cにでも移植しよう。

2016年12月21日

[py]コンソールログを出力させたい

多少pythonにも慣れてきて、ことあるごとに出力させていたprintが邪魔になってきた。
が、全部消してしまうと困るかもしれないし、大幅に変更させるときにはまた復活させたくなるかもしれない。

そうなると、デバッグレベルに応じて出力させたりさせなかったりを制御したい。


「python ログ」で検索すると、こちらが出てきた(私はまだpython2.7を使っているのだ)。

Logging HOWTO — Python 2.7.x ドキュメント

「ロギングレベルをコマンドラインオプションから~」とあるので、pythonにそういうオプションがあるのかと思ったが、単に引数を解析してからやるだけのようだった。
もしかしたら、getoptみたいな関数があるのかもしれんが、調べる気力が無いので、第1引数をそのままロギングレベルと見なすように書いてみた。

import logging
import sys

loglevel = sys.argv[1]
# print 'getattr=', getattr(logging, loglevel.upper(), None)
numeric_level = getattr(logging, loglevel.upper(), None)
if not isinstance(numeric_level, int):
    raise ValueError('Invalid log level: %s' % loglevel)
logging.basicConfig(level=numeric_level)

logging.debug('debug')
logging.info('info')
logging.warning('warning')

INFO、とするとinfoが出るし、DEBUGとすればdebugも出てきた。
コンソール出力を「> log.txt」にしても空っぽで、「2>&1」を追加すると入ったところからすると、標準エラー出力がデフォルトのようである。
まあ、ファイルに保存する設定もあるから、そこまでリダイレクトしたいことはないのかもしれんがね。


これくらいは用は足りそうなのだが、loggingページのまだ最初の方だ。
もっと便利な何かがあるのだろうか?

変数の表示が%で指定するタイプだ、とか、時刻を入れるフォーマットにできるよ、とか、そういうのがちょっと書いてあって、あとは上級編になるようだ。

ちょっとだけ眺めてみたが、loggingは、logger, handler, filter, formatter、の4つから構成されているようだ。
こんなに複雑だったのか・・・。
自分でいろいろ指示することによって、カスタマイズできるようになっているようで、クックブックのページも用意されていた。

Logging クックブック — Python 2.7.x ドキュメント

 

まあ、そこまで踏み込まなくていいな。

  • 地の文:print
  • 普段はいらない、細かいデバッグ出力:logging.debug()
  • 問題ないかもしれないけど念のため忠告の出力:logging.info()
  • ちょっとないかもねー、バグってんじゃないの?の出力:logging.warning()
  • これはダメだー、の出力:logging.error()

私の場合は、debugとerrorがあればまだ足りるかな。
errorも、その後はquit()で終わらせるしかないなら、もうprintでもよい気がする。
ログ出力できるほど優雅じゃ無い環境が多いので、自由に出してよいといわれると困るのよねぇ。

2016年12月10日

[py]sqlite3のNoneは文字列のNone

久々のpythonで、ちょっとはまってしまった。
コードを先に示した方がよいだろう。


import sqlite3

dbconn = sqlite3.connect('dbtest.db')
dbcur = dbconn.cursor()
dbcur.execute('CREATE TABLE test(id1 INTEGER, id2 INTEGER)')
dbcur.execute('INSERT INTO test(id1) VALUES(?)', (1234,))
dbcur.execute('SELECT * from test')
items = dbcur.fetchall()

print 'item[0]=', str(items[0][0]).encode('hex')
print 'item[1]=', str(items[0][1]).encode('hex')


id1とid2というカラム(でよいのかな?)を作り、id1にだけ値を設定する。
そしてSELECTして、id1とid2をencode('hex')して見てみる、というだけだ。

encode('hex')だから、ASCII文字はASCIIコードで出力される。
id2には何も設定していないが、どうなるだろうか?

item[0]= 31323334
item[1]= 4e6f6e65

さあ、予想通りだっただろうか?

item[0]はよそうどおりだったけど、item[1]は「そうくるか!」というのが正直な感想だ。
まさか、"None"を文字列としてASCIIコードにするとはなぁ。。。。

 

おかげで、"None"と出てくるか、エラーになるだろうと思ってやっていたので、発見が遅れてしまった。。。

2016年11月29日

[py]buffer()とbytearray()の違いはなんだろう?

よくわからないまま、どこかからコピーしたコードを使うことがしばしばある。
それによって、これってなんだろう?という疑問が湧くこともしばしば。

 

いま、paho-mqttでpublish()するところを見ていたのだが、こんな書き方をしていた。

publish(topic, buffer(data1) + bytearray(data2))

なんとなく、buffer()もbytearray()も似たようなことをしてくれそうだ。
何が違うのだろう?

 

以前、16進数の配列を作る際に、こういうやり方があることを知った。

cmd = bytearray([0x03, 0x12, 0x34, 0x56])

これだと、cmd[1]などで数値が取ってこれるので、そのままif文や添字として使える。
bytearrayは組み込み型で、バイト配列を返すシーケンスとのこと。

 

一方、bufferは毛色がなんか違う。
bufferオブジェクト、なのだ。
CのAPIを使うときなどに出てくるようなので、素のバイト配列なのだろう。

 

data1 = 15
data2 = '1234abcd'.decode('hex')

buf = buffer(chr(data1)) + buffer(data2)
print 'buf=', buf.encode('hex')

dat1 = ord(buf[0])
dat2 = buf[1:]
print 'dat1=', dat1
print 'dat2=', dat2.encode('hex')

buf= 0f1234abcd
dat1= 15
dat2= 1234abcd

data1 = 15
data2 = '1234abcd'.decode('hex')

buf = bytearray(chr(data1)) + bytearray(data2)
print 'buf=', str(buf).encode('hex')

dat1 = buf[0]
dat2 = buf[1:]
print 'dat1=', dat1
print 'dat2=', str(dat2).encode('hex')

buf= 0f1234abcd
dat1= 15
dat2= 1234abcd

 

bytearrayはencode('hex')がないし、str型ではないからstr()で囲んだり、ord()が不要だったりという違いがある。

 

data1 = 15
data2 = '1234abcd'.decode('hex')

buf = buffer(chr(data1)) + bytearray(data2)
print 'buf=', buf.encode('hex')

dat1 = ord(buf[0])
dat2 = buf[1:]
print 'dat1=', dat1
print 'dat2=', dat2.encode('hex')

buf= 0f1234abcd
dat1= 15
dat2= 1234abcd

混合できて、その場合はstr型と同じ扱いになるようだ。

 

paho-mqttのpublish()は、payloadとして文字列を使いたいようだ。
intとか送りたかったらstruct.pack()してくれ、と書いているし。
bytearray型を引数にして使えているのだが、これはpahoがstr()で囲んだりしてくれているからかもしれん。

・・・と思ったが、違った。
bufferを与えると例外が発生したのだ。

TypeError: payload must be a string, bytearray, int, float or None.

bytearrayは許可してたのね。。。
そしてintはstruct.pack()してくれ、だと思ったのだけど、例外の内容を見るとintも許可しているような感じだな。
まあ、使わないから調べないけど。

2016年11月28日

[py3]2to3ではdecode/encode('hex')などは変換してくれないようだ

さて、いま作っているプロジェクトもpython3対応しようと、2to3.pyにかけてみた。
ワイルドカードで指定できなかったので、cygwinでxargsを使ったが、printがprint()になっているので変換できたのだろう。
よし、とpyCharmの設定もpython3にして開いてみると、指摘がたくさん。。。

こんなのが指摘されている。

struct.pack('<H', length).encode('hex')

bytes = lenstr.decode('hex')

 

hiro99ma blog: [py]python3は16進数の扱いが違うんだ

違うんだ、を実感した瞬間である。


decode/encodeはstr型用だから、bytes型だと使えない。
struct.pack()はbytes型で返すようになっている。
だから、bytes型の扱いに変換すればよいだけのことだ。

文字.decode('hex')は、bytes.fromhex(文字)にすればよいはずだ。
そこはそれで済みそうだが、"\xab\x12"のようにして作っていた文字列をbytes型に変換しないと、fromhex()したデータとの足し算というか連結ができないことがわかった。
これは、文字列の前に「b」を付けるだけでよいようだ。

と、やってみたのだが、bytesの要素をord()したりstruct.unpack()すると怒られる。。。

ord(cmd_bytes[0])

strがほしいのにintが渡されている、ということだった。
デバッガで見てみると、bytes配列の要素はint型になっていた。
そうか、ord()しなくてもいいんだ。。。

が、これはこれで別の問題が起きた。
辞書のkeyとして"\xab"のような値を使っていたのだが、これをbytes型にしないと検索できなくなった。
しかし、検索するのはbytesから1バイト取ってきたデータなので、そのままだとint型になってしまう。
よくわからんので、cmd_bytes[0].to_bytes(1, byteorder='little')、などとしてbytes型に変換して、動きはしたのだけど、ダサい。。。

4. 組み込み型 — Python 3.5.2 ドキュメント
なるほど、要素として取り出すとintになるから、サイズ1の配列として取り出せばよいのか。

cmd = cmd_bytes[pos: pos+1]

こんなのでよい。
数値にしたければ、cmd[0]とすればよいだけなので、こっちの方が扱いやすいな。

 

encode('hex')するのは目で読める文字にするとき、decode('hex')するのは実際のデータにするとき、という使い分けをしていた。
これは、bytes型にhex()というメソッドがあり、それでよかった。


私がbytes型を使うときのやり方をまとめると、こう。

  • 文字列でエスケープシーケンスを使って16進数を表していた→あたまに「b」を付ける
  • decode('hex')していた→bytes.fromhex()にする
  • ord()していた→bytesを1要素として取り出す(data[3]など)
  • bytes型として切り出したい→配列として取り出す(data[3:4]など)
  • encode('hex')していた→hex()にする

struct.pack/unpack()は、to_bytes()もあるし、あまり使わないかも。
to_bytes()の逆はなさそうだから、unpack()は使うことになるか。。。あ、int.from_bytes()というのもあるようだ。
to_bytes()はメソッドなのに、from_bytes()はstaticメソッドなのはなんでだろう?

 

pyCharmで指摘が出ているのは、あからさまにbytes型になったと分かる箇所だけだ。
型指定がないから、引数でもらったものをdecode('hex')などとしていても、そこは出てない。
だから、たくさん指摘が出ていると思ったけど、氷山の一角なのだ。
2to3でやってくれよーと思ったのだが、これはちょっと無理だな。

 

また、変換しようとしていたpythonプロジェクトは、str型のbytearrayで扱う場合と、str型で16進数文字列を扱う場合が混在していて、あっちではstruct.pack/unpack、こっちではdecode/encode('hex')と、実にまとまりがない。

今回は、python3にするのだったら、もう一度書き直した方が早そうだ。。。

2016年11月27日

[pyCharm]フォルダが表示されなくなったら、Content Rootを疑う

なぜか、pyCharmのプロジェクトツリーにフォルダが表示されなくなった。
先週は普通に使っていた気がするのだが、何をしたのか記憶にない。。。

 

あれこれ見てみたが、Settingsダイアログを開き、「Project:xxx > Project Structure」を見ると、Add Content Rootのところが赤文字になっていた。
よくわからないながらも、一度削除して、また追加すると直った。

 

困ったことに、これはUNCパスの場合はうまくいかなかった。
結局、一度Windowsのローカルフォルダに移動させ、pyCharmの設定を修正して、またUNC上に戻すことになった。

まあ、推奨していないから仕方ないんだけどね。

2016年11月26日

[py2]feature無きprint()は、タプル扱い

「動力無きK村」(炎色反応の覚え方)みたいだが、その話ではない。

python2ではステートメントのprintがあるから、組み込み関数のprint()が使いたい場合にはimport文を書くように、となっている。
28.4. future_builtins — Python 3 のビルトイン — Python 2.7.x ドキュメント

が、python2.7でprint()とprintを両方書いたが、どちらも動いた。
なぜだろうか?

 

PEP 3105 -- Make print a function | Python.org
featureのimportを書いていない場合は、print()の関数、ではなく、printステートメント+タプル、という動作になっているということか?

print('Hello! I\'m Python2', 'not feature')

これをpython2.7.12で実行すると、こうなった。

("Hello! I'm Python2", 'not feature')

python3.5.2だと、こうなる。

Hello! I'm Python2 not feature

なるほどねぇ。

[py3]2to3で変換してみよう

作業場にて。。

Aさん「このpython、バージョンは何で書いてるの?」
私「2.7です」
Aさん「3じゃないんだ。。。」

というわけで、そろそろpython3を視野に入れないといかんようだ。

 

python2.7をインストールすると、Tools\Scriptsの中に2to3.pyというスクリプトがある。
これがpython2をpython3に変換してくれるツールらしいので、試してみよう。


print 'Hello! I\'m Python2'


単純すぎで済まん。。。
私が思いつくpython2と3の違いは、printで括弧がいるかどうかなのだ。

これを「python2.py」というファイル名で保存して、2to3にかけてみる。
うちはWindowsで、python2.7はC:\Winappli\Python27というフォルダにインストールしている。


C:\Prog\python\py3test>python C:\Winappli\Python27\Tools\Scripts\2to3.py python2.py
RefactoringTool: Skipping optional fixer: buffer
RefactoringTool: Skipping optional fixer: idioms
RefactoringTool: Skipping optional fixer: set_literal
RefactoringTool: Skipping optional fixer: ws_comma
RefactoringTool: Refactored python2.py
--- python2.py  (original)
+++ python2.py  (refactored)
@@ -1 +1 @@
-print 'Hello! I\'m Python2'
+print('Hello! I\'m Python2')
RefactoringTool: Files that need to be modified:
RefactoringTool: python2.py


オプションを付けないと、差分だけ表示するが、-wを付けるとファイルに書き込んでくれる。
前のファイルは.bakを付けて残される。


print('Hello! I\'m Python2')


まあ、予想通りの結果だ。
python3で実行しても動く。
めでたしめでたし。

 

ではあるのだが、これをpython2で動かしても、ちゃんと動くのだ。
じゃあ、最初からprintも括弧を付けて書いておけばいいのに、なんでそういうサンプルになっていなかったのだろう?

軽く調べたところ、python2で括弧無しのprintは、passやbreakと同じ「print」というステートメントという扱いらしい。
6. 単純文 (simple statement) — Python 2.7.x ドキュメント

そして、2.7の方で動いたのは組み込み関数のprint()なのだが、これは2.6から追加されているようだ。
2. 組み込み関数 — Python 2.7.x ドキュメント

python2では当初print()関数がなかったから、ステートメントのprintで書いているため、それを見て覚えた人がまたステートメントのprintを使って。。。ということで、脈々と受け継がれたということだろう。

ああ、新参者の私には分からぬ事情であったことだよ。

2016年11月24日

[py]pyCharmでUNCのパスを開くのは推奨していないそうだ

冬になり、デスクトップ環境だと足が寒くなった。
そうなると、自然とコタツ開発になってくる。

image

 

今作っているpythonスクリプトはLinux上で動かす前提だ。
うちはWindowsメインなので、VirtualBox上でLinuxを動かしている。
そして、そこにpythonスクリプトも置いている。

コタツにはノートPCがあり、そこからVirtualBox上の環境にアクセスするには、VirtualBoxのネットワーク設定を「ブリッジアダプター」にせねばならなかった。
「ホストオンリー」だとデスクトップ環境からしかアクセスできないのだ。

 

そうやってノートPCからVirtualBox上のLinux環境にアクセスできるようになった(Linux上ではSambaを動かしている)。
pyCharmで作ったプロジェクトも、ごそっとLinux上に移動させた。

そこまではよかったのだが、Windowsでネットワーク上のファイルアクセスする場合は\\serverのようなUNC名でアクセスすることになるのだが、pyCharmのプロジェクトを開くダイアログでUNCのパスを入力してもアクセスできないのだ。

 

検索したところ、pyCharmというかJetBrain系のツールかもしれんが、UNCでのアクセスは推奨していないようだった。
ネットワークドライブを設定してくれ、というような回答が見つかった。

しかし、それはめんどうなので、UNCでアクセスすることにした。


やり方は難しくなくて、ExploreからUNC先のフォルダを開き、そのpythonファイルをpyCharmで開くだけだ。
UNCのところには、あらかじめpyCharmのプロジェクトファイルを置いていなくてはならないだろう。
だから、一度ローカルでプロジェクトを作って、UNC先にコピーすることになるかな。

pyCharmで開くのは、pyファイルに関連づけていなければ、コンテキストメニューの「プログラムから開く」で選べばよいだろう。

一度開くと、pyCharmのプロジェクト一覧にはUNCの名前でも残るので、それをアクセスすればよい。
まあ、推奨されていないので正しく動かなくても知らんけどね。
うちの場合は実行がローカル環境じゃないので、単なるエディタとして使っているが、今のところ問題なさそうだ。
もちろん、Linux先のpython環境を参照するわけではないので、importでローカルに入っていないものがあればエラーが出てしまうが、そういうのはなんとかしよう。

2016年11月23日

[py]1バイト分のstrを数値に変換するならord()が使える

先頭にデータ長が付いたstr型のデータを扱うことが多い。
こんなのだ。

cmd = '\x03\x12\x34\x56'

Cだったら、先頭のデータ長を取ってきたい場合、こうするだろう。

cmd = "\x03\x12\x34\x56";
uint8_t length = (uint8_t)cmd[0];

pythonだと、いつもこうやっている。

cmd = '\x03\x12\x34\x56'
length = struct.unpack('B', cmd[0])[0]

 

これが、ぱっと頭に思いつかない。。
そして、長い。
他に方法はないのだろうか?


1つ私が勘違いしていたのは、16進数でデータを突っ込んだ文字列はbytearray型だと思い込んでいたことだ。
文字列は、文字列だ。だからstr型になる。

これがッ、bytearray型だッ!

cmd = bytearray([0x03, 0x12, 0x34, 0x56])

まあ、他に記述方法があるのかもしれんが、見つけたのがこの書き方だった。
Cの書き方に近い。

この書き方の利点は、数値を取ってくることが可能なところだ。

cmd = bytearray([0x03, 0x12, 0x34, 0x56])
print 'cmd[0]=', cmd[0]

期待通り!

ただ、str型ではないので、こういうのはできない。

print 'cmd=', cmd.encode('hex')

が、これならいける。

print 'cmd=', str(cmd).encode('hex')

 

そうか。。。SQLiteで取ってきたBLOB型をstrして使っていたが、こういう意味だったのか・・・。
そういえば、BLOB型にINSERTとかするときも、bytearrayで囲んでいるし。

 

今はstrベースでやっているので、こういう感じで使うのがよさそうだ。

cmd = '\x03\x12\x34\x56'
print 'cmd=', cmd.encode('hex')
length = bytearray(cmd)[0]
cmd_byte = cmd[1: 1 + length]
print 'cmd_byte=', cmd_byte.encode('hex')

これだと、3バイト分表示されるので、これでいこう。

lengthに代入するのが「bytearray(cmd[0])」だとダメだった。
1バイト分取ってくるという意味では同じそうに見えるのだが。。。


などとごにょごにょ調べていたら、もう1つ別の方法を見つけた。
ord()だ。

length = ord(cmd[0])

こういうのを求めてたんだよ!

 

逆のパターンの、数値を文字列化する場合はchr()だそうだ。
今までこうやって書いていたのだけど、

%02x' % length

これでよいことになる(lengthの範囲によるけど)。

chr(length)

こう書くと、「cmd2= 02789a」となる。

cmd2 = chr(2) + chr(0x78) + chr(0x9a)
print 'cmd2=', cmd2.encode('hex')

 

組み込み関数は、私が求めているものが多そうなので気に掛けておかねば。