質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.13%

エラーになる理由を知りたい

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 5,838

gori

score 17

前提・実現したいこと

https://github.com/appdamacy/Grape

上記ソースコードで
return template('index', row=row)となっている箇所を
return template('index', row)とした場合にエラーになる理由を知りたいです。
row=rowとすることでどういった処理が行われているのでしょうか。

発生している問題・エラーメッセージ

ValueError('dictionary update sequence element #0 has length 4; 2 is required',)

該当のソースコード

import sqlite3
import feedparser
from bottle import route, run, template, static_file, debug
debug(True)

@route('/static/<filepath:path>')
def static(filepath):
    return static_file(filepath, root="./static")

@route('/hello')
def hello():

    con = sqlite3.connect('grape.db')
    c = con.cursor()
    c.execute("create table if not exists item (title, link, summary, updated)")
    url = [
            "https://lineblog.me/sakuraihinako/atom.xml",
            "https://lineblog.me/masuwakatsubasa/atom.xml",
            "https://lineblog.me/funayamakumiko/atom.xml"
        ]
    feed = []
    for rss in url:
        feed.extend(feedparser.parse(rss).entries)
    for item in feed:
        title = item.title
        link = item.link
        summary = item.summary
        updated = item.updated
        c.execute('INSERT INTO item VALUES (?, ?, ?, ?)', (title, link, summary, updated))
    c.execute(u"select * from item order by updated desc")
    row = []
    for item in c:
        row.append({
            "title": item[0],
            "link": item[1],
            "summary": item[2],
            "updated": item[3]
        })
    c.close()

    return template('index', row)

run(host='localhost', port=8080)

試したこと

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • quiqui

    2017/07/17 22:44

    pythonのコードはインデントがないと意味が取れないので、前後行を ``` で括って正しく表示されるようにした方がいいですよ。

    キャンセル

回答 3

checkベストアンサー

+3

Python では関数に渡す引数として、位置引数 と キーワード引数 の2種類があります。参考:argument — Python 3.6.1 ドキュメント

row と値だけ渡すのが位置引数、 row=row とキーを指定するのがキーワード引数です。

キーワード引数は、デフォルト値を持った位置引数の一部を飛ばしたいときに使ったり、何を渡しているのか判りやすくするためにあえてキーを付けることがあります。

def cube(x=1, y=1, z=1):
    print('x:{}, y:{}, z:{}'.format(x, y, z))

cube(y=10, z=20)  # x の定義を省略
add_cart(1, 12345, 10)  # なにを渡しているのかわからない

add_cart(userid=1, itemid=12345, quantity=10)  # わかりやすい

ところで関数の引数定義部分で *hoge でまだ格納されていないすべての位置引数を、**hoge でまだ格納されていないすべてのキーワード引数を格納させることができます。このようにして受け取る引数の数をその時々に応じて変えられる仕組みを可変長引数と言います。

ここで Bottleのソースコード をみると、def template(*args, **kwargs): とやって可変長位置引数と可変長キーワード引数で値を受け取って、return TEMPLATES[tplid].render(kwargs) とやって render に可変長キーワード引数を渡していることが判ります。

そもそも Bottle の bottle.template 関数に row なんて引数は存在しませんよね? それはあなたの考えた変数です。それを Bottle は可変長キーワード引数として受け取り、レンダラーに渡し、レンダラーは row というキーを使って値を取り出しテンプレートに埋め込んでHTMLを生成しています。


ここから蛇足。

bottle.template は第2位置引数以降に 辞書を渡すことによっても、レンダラーに値を渡すことができます。

"""キーワード引数で値を渡す場合"""
template('index', row=row, name=name)

"""位置引数で辞書を渡す場合"""
values = {'row': row, 'name': name}
template('index', values)

"""要はアンパック渡ししているのとと"""
values = {'row': row, 'name': name}
template('index', **values)

この仕様があるため、template('index', row) としたときに第2位置引数に辞書ではない row オブジェクトを渡してしまっているためにエラーが起きます。

まあ今回のキモは "キーワード引数"、"可変長引数" ですからエラーが出るメカニズムを追う必要はないと思います。ただ Bottle は設計やコードがきれいで手本として最適とよく言われています。より Python を深く理解したくなったら Bottle のソースコードを読んで学んでみるとよいかもしれません。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/07/18 00:50

    ほかの方とだだかぶりしてしまいました。。。すみません。

    キャンセル

  • 2017/07/18 19:41

    回答ありがとうございます。
    可変長引数を理解できていませんでした。引数を正しく設定できていなかったんですね。
    やっと理解できました。

    キャンセル

+2

気になって少し調べてみました。まず、templateの実装

def template(*args, **kwargs):
tpl = args[0] if args else None
    for dictarg in args[1:]:
        kwargs.update(dictarg)   # ←ここ!

エラーが発生しているのはupdateですね。


次に、(*args, **kwargs)の意味についての解説

>>> def hoge(*args, **kwargs):
...     print args
...     print kwargs
...
>>> hoge(1, 2, 3, ['a', 'b', 'c'], name='my_name', data='100')
(1, 2, 3, ['a', 'b', 'c'])
{'data': '100', 'name': 'my_name'}

キーワードを指定しないとargsに、指定するとkwargsに渡されるのですね。


さらに、updateメソッドの使い方

dict['氏名'].update({'追加科目名1' : '点数', '追加科目名2' : '新点数'})

updateメソッドの引数は辞書型なのですね。


結局なんでエラーが生じているのか
さっきのコードをもっかい見てみます。

def template(*args, **kwargs):
tpl = args[0] if args else None
    for dictarg in args[1:]:
        kwargs.update(dictarg)   # ←ここ!

argsで取得した値を、kwargsにマージしているようです。
updateメソッドの引数が辞書型でないので、エラーが生じているのでしょう。

試しに、次のようにしたら動きませんか?

return template('index', {'row': row})

実験してみました

def func(*args, **kwargs):
    for dictarg in args:
        kwargs.update(dictarg)

    print(kwargs)

tmplist = list(range(4))
func(a=tmplist)
func({'b': tmplist})
func(tmplist)
""" 実行結果
{'a': [0, 1, 2, 3]}
{'b': [0, 1, 2, 3]}
Traceback (most recent call last):
  File "tmptest.py", line 10, in <module>
    func(tmplist)
  File "tmptest.py", line 3, in func
    kwargs.update(dictarg)
TypeError: cannot convert dictionary update sequence element #0 to a sequence
"""

うん、予想通り。


色んな引数の与え方があるんですね。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/07/17 23:33

    回答ありがとうございます。なんとか理解できたと思います。
    rowの型がリストとなっていたのでエラーとなったところまでは理解できました。
    row=rowとすることで型がリストから辞書型に変換されたのはなぜでしょうか?
    回答の際に例として記載していた下記のように記載する必要があると思うのですが・・
    回答いただけないでしょうか。

    return template('index', {'row': row})

    キャンセル

  • 2017/07/17 23:46

    キーワードを指定
    ・しない -> args
    ・する  -> wkargs

    キャンセル

  • 2017/07/17 23:46

    wkargsに渡されたときは、自動的に辞書として扱われるようですね。

    キャンセル

  • 2017/07/18 19:43

    よくやく理解できました。引数にも色んな種類があるんですね。
    ライブラリの中身まで含めて解説いただきありがとうございました。

    キャンセル

0

対象のコードは読めてませんが、エラーでいえばここが参考になると思います。
https://stackoverflow.com/questions/14302248/dictionary-update-sequence-element-0-has-length-3-2-is-required

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/07/17 22:34

    エラーコードで検索して似たようなページにいくつかたどり着きましたが、理解できませんでした。

    キャンセル

  • 2017/07/17 22:38

    具体的に何が理解できなかったのかを記述すると回答が得られやすくなりますよ。

    キャンセル

  • 2017/07/17 22:41

    rowをrow=rowとすることでどう変化するのかが分かりませんでした。

    キャンセル

  • 2017/07/17 22:42

    質問に追記します。ありがとうございます。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.13%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる