ベスパリブ

プログラミングを主とした日記・備忘録です。ベスパ持ってないです。

リストの要素がクラスオブジェクトの場合のソート(Python)

リストの要素がクラスオブジェクトの場合に、そのオブジェクトのメンバ変数に対してソートをしたい。

クラスに対するソート(と言ったら語弊があるけど)。

やりたいこと

class Person():
    
    def __init__(self, _id, height, name):
        self._id = _id
        self._height = height
        self._name = name

def solve():
    N = 5
    p = [None] * N
    p[0] = Person(5, 160, "うえだ")
    p[1] = Person(2, 140, "いいだ")
    p[2] = Person(4, 140, "えのもと")
    p[3] = Person(1, 150, "あすのざか")
    p[4] = Person(3, 160, "おおの")

    # このとき、pをPersonのid順などでソートしたい

def __name__ == "__main__":
    solve()

コード

# coding: utf-8
class Person():
    
    def __init__(self, _id, height, name):
        self._id = _id
        self._height = height
        self._name = name
        self.latest_me = None # 最近振った目の数

    @property
    def id(self):
        return self._id
    
    @property
    def height(self):
        return self._height
    
    @property
    def name(self):
        return self._name
    
    def toss_dice(self):
        """ 1~6の目のサイコロを振り、出た目の数を返す """
        import random
        self.latest_me = random.randrange(1, 7)
        return self.latest_me


def sort_height(p, N):
    print("--背の順でソートする--")

    # lambdaを使う場合
    sorted_p = sorted(p, key=lambda person: person.height)

    # operator.attrgetterを使う場合(結果はlambdaを使った場合と同じ)
    from operator import attrgetter
    sorted_p = sorted(p, key=attrgetter("height"))

    for i in range(N):
        print("{}, {}, {}".format(sorted_p[i].id, sorted_p[i].height, sorted_p[i].name))


def sort_height_and_id(p, N):
    print("--背の順でソートし、かつ、idが若い順にソートする(身長が同じ人はidが若い順に並ぶ)--")
    
    from operator import attrgetter
    sorted_p = sorted(p, key=attrgetter("height", "id"))

    for i in range(N):
        print("{}, {}, {}".format(sorted_p[i].id, sorted_p[i].height, sorted_p[i].name))


def sort_dice(p, N):
    print("--サイコロを振り、出た目の数でソートする(実行するたびに変わる)--")
    
    # lambdaを使う場合
    sorted_p = sorted(p, key=lambda person: person.toss_dice())
    
    # operator.methodcallerを使う場合(結果はlambdaを使った場合と同じ)
    from operator import methodcaller
    sorted_p = sorted(p, key=methodcaller("toss_dice"))

    for i in range(N):
        print("{}, {}, {} : D{}".format(sorted_p[i].id, sorted_p[i].height, sorted_p[i].name, sorted_p[i].latest_me))


def solve():
    N = 5
    p = [None] * N
    p[0] = Person(5, 160, "うえだ")
    p[1] = Person(2, 140, "いいだ")
    p[2] = Person(4, 140, "えのもと")
    p[3] = Person(1, 150, "あすのざか")
    p[4] = Person(3, 160, "おおの")

    # 単純に表示する
    for i in range(N):
        print("{}, {}, {}".format(p[i].id, p[i].height, p[i].name))
    
    # 背の順でソート
    sort_height(p, N)

    # 背の順でソートし、かつ、idが若い順にソートする(身長が同じ人はidが若い順に並ぶ)
    sort_height_and_id(p, N)

    # サイコロを振り、出た目の数でソートする(実行するたびに変わる)
    sort_dice(p, N)

if __name__ == "__main__":
    solve()

↓結果

5, 160, うえだ
2, 140, いいだ
4, 140, えのもと
1, 150, あすのざか
3, 160, おおの
--背の順でソートする--
2, 140, いいだ
4, 140, えのもと
1, 150, あすのざか
5, 160, うえだ
3, 160, おおの
--背の順でソートし、かつ、idが若い順にソートする(身長が同じ人はidが若い順に並ぶ)--
2, 140, いいだ
4, 140, えのもと
1, 150, あすのざか
3, 160, おおの
5, 160, うえだ
--サイコロを振り、出た目の数でソートする(実行するたびに変わる)--
2, 140, いいだ : D1
3, 160, おおの : D1
5, 160, うえだ : D3
4, 140, えのもと : D3
1, 150, あすのざか : D4

おまけ

参考URL:

docs.python.org

海底ケーブルマップがすごい

Django GirlsのREADMEを読んでいて、インターネットの仕組みの項目を読んでいた。

そこに海底ケーブルマップのサイトのリンクがあった。

Submarine Cable Map

すごい。色々すごいけど、志摩市の海底ケーブルの本数がすごい。志摩スペイン村しか知らなかったのに。

オホーツク海のケーブルや、クック諸島にぽつーんとケーブル引かれてるのが見てて面白い。

海底ケーブルって途方がなさすぎて全然イメージできないんですけど、こんなに張られてたんですねぇ。あと地下鉄感がすごい。

f:id:takeg:20190305172520p:plain
志摩の海底ケーブル

f:id:takeg:20190305172551p:plain
オホーツクの大三角形

f:id:takeg:20190305172618p:plain
太平洋にインターネットを届けている

ラズパイでgpartedを実行、"e2fsck cannot continue aborting is mounted"エラー

Raspbian用のSDカードの容量を小さくしたかった。

以前にもやったので、その方法を備忘録として残していました。試行錯誤の結果、以下の記事のまんまやればよいことがわかりました。

qiita.com

で、やってたらエラーが起きました。エラーの詳細を読むと、以下のような感じのことが書いてありました。

e2fsck cannot continue aborting is mounted

/dev/sda2 is mounted

/dev/sda2をアンマウントしてるのにおかしいなーと思ってたんですが、小一時間悩んで理由がわかりました。

私は以下のような構成でやっていました。

  • ラズパイ(起動中。gpartedを実行する。コピー元のSDカードでもある)
  • コピー先のSDカードをUSBに挿す

これではラズパイは起動中なので実際にはアンマウントできていないのが原因でした(たぶん)。

本当に必要な構成は、

  • ラズパイ(起動中。gpartedを実行する)
  • コピー元のSDカードをUSBに挿す
  • コピー先のSDカードをUSBに挿す

でした。

まあ記事をちゃんと読むとそう書いてあるんですけど、以前にやったことあったので適当に流し読みしてしまっていました。気を付けよう。

(SDカードのリーダライタが大きくて、ラズパイのUSBにそのまま2つ挿せなくて延長ケーブルが必要になって面倒くさかった)

Pythonの__file__は、対話モードで実行するとエラーになる

Pythonで、ファイル名を出力してくれる__file__は、対話モードで実行するとエラーになります。

> python
Python 3.7.1 (default, Dec 10 2018, 22:54:23) [MSC v.1915 64 bit (AMD64)] :: Anaconda, Inc. on win32
Type "help", "copyright", "credits" or "license" for more information.

>>> print(__file__)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name '__file__' is not defined

対話モードなので、ファイルなんてないので当然といえば当然なエラーでしょうか。

ファイルで実行するときちんと出力してくれます。

# sample.py
import os

print(__file__)

a = os.path.dirname(os.path.abspath(__file__))
print(a)
a = os.path.dirname(os.path.abspath("__file__"))
print(a)
> python sample.py
sample.py
C:\Users\XXXX\workspace
C:\Users\XXXX\workspace

Bitbucketでブランチのブランチタイプを変更する

Bitbucketで、masterブランチのブランチタイプ(Branch type)がMAINかつDEVELOPMENTになっていたので、developブランチをDEVELOPMENTにしたいという話です。

f:id:takeg:20190131152325p:plain
masterブランチがMAINかつDEVELOPMENT

設定 > Branching model で、Development branchを変更して、保存します。

f:id:takeg:20190131152408p:plain
Deveropment branchの変更

ブランチ画面に行き、ブランチタイプの変更ができていることを確認します。

f:id:takeg:20190131152704p:plain
developブランチがDEVELOPMENT

ブランチタイプの説明は以下の公式に詳しく書いてあります。

ja.confluence.atlassian.com

はてなブログをHTTPS化した

私のはてなブログHTTPSじゃなくて、「いつになったらはてなHTTPS対応してくれるんだろーー~~~~~~~~~~ばなな」って口を開けて待ってたんですが、普通に公式からアナウンスがありました。

help.hatenablog.com

移行方法

ダッシュボードから設定画面にアクセスし、「変更する」を選択してください。

ワンクリックでHTTPS化できました。はてなさんありがとう。

Access to bucket "staging.PROJECT_ID.appspot.com" denied. You must grant Storage Object Viewer permission to ... エラーの解消

Google App EngineHello Worldチュートリアルを進めていたらデプロイ中にエラーが発生しました。

(python_docs_samples) C:\Users\XXXX\workspace\python-docs-samples\appengine\standard_python37\hello_world>gcloud app deploy
Services to deploy:

descriptor:      [C:\Users\XXXX\workspace\python-docs-samples\appengine\standard_python37\hello_world\app.yaml]
source:          [C:\Users\XXXX\workspace\python-docs-samples\appengine\standard_python37\hello_world]
target project:  [PROJECT_ID]
target service:  [default]
target version:  [20190126t190818]
target url:      [https:/PROJECT_ID.appspot.com]


Do you want to continue (Y/n)?  Y

Beginning deployment of service [default]...
#============================================================#
#= Uploading 5 files to Google Cloud Storage                =#
#============================================================#
File upload done.
Updating service [default]...failed.
ERROR: (gcloud.app.deploy) Error Response: [9] Cloud build AA-BB-CC-DD-EEEEE status: FAILURE.
Build error details: Access to bucket "staging.PROJECT_ID.appspot.com" denied. You must grant Storage Object Viewer permission to 9XXXXXXXX8@cloudbuild.gserviceaccount.com.
.
Check the build log for errors: https://console.cloud.google.com/gcr/builds/AA-BB-CC-DD-EEEEE?project=9XXXXXXXX8

注目すべきエラーメッセージは Build error details: Access to bucket "staging.PROJECT_ID.appspot.com" denied. You must grant Storage Object Viewer permission to 9XXXXXXXX8@cloudbuild.gserviceaccount.com. . の部分で、「バケットへのアクセスが失敗」と言われています。

さらに「9XXXXXXXX8@cloudbuild.gserviceaccount.com」に「Storage Object Viewer権限を与えてくれ」と言われています。

権限のエラーはIAMをいじれば解消できるはず。IAMの設定をろくにしたことがないので以下のリンクを読みます(バケットに対するIAMについても書いてあります)。

Using (Cloud IAM) Permissions  |  Cloud Storage  |  Google Cloud

なんとなくわかったところで、GCPから「IAMと管理」の画面を開き、9XXXXXXXX8@cloudbuild.gserviceaccount.comの編集アイコンをクリックします。

f:id:takeg:20190128160804p:plain
9XXXXXXXX8@cloudbuild.gserviceaccount.comの編集アイコンをクリック

「別の役割を追加」で「ストレージオブジェクトの閲覧者」を追加します。

f:id:takeg:20190128160910p:plain
「ストレージオブジェクトの閲覧者」を追加

そして再度デプロイするといけるはず……たぶん。

(python_docs_samples) C:\Users\XXXX\workspace\python-docs-samples\appengine\standard_python37\hello_world>gcloud app deploy
Services to deploy:

descriptor:      [C:\Users\XXXX\workspace\python-docs-samples\appengine\standard_python37\hello_world\app.yaml]
source:          [C:\Users\XXXX\workspace\python-docs-samples\appengine\standard_python37\hello_world]
target project:  [PROJECT_ID]
target service:  [default]
target version:  [20190126t193649]
target url:      [https://PROJECT_ID.appspot.com]


Do you want to continue (Y/n)?  Y

Beginning deployment of service [default]...
#============================================================#
#= Uploading 0 files to Google Cloud Storage                =#
#============================================================#
File upload done.
Updating service [default]...done.
Setting traffic split for service [default]...done.
Deployed service [default] to [https://PROJECT_ID.appspot.com]

You can stream logs from the command line by running:
  $ gcloud app logs tail -s default

To view your application in the web browser run:
  $ gcloud app browse

デプロイできました!

gcloud app browseを実行するとWEBアプリケーションにアクセスできます。