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

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

新規登録して質問してみよう
ただいま回答率
85.50%
Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

886閲覧

pythonで関数の戻り値をprintしたい

takechanman

総合スコア2

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2021/10/29 18:12

前提・実現したいこと

リスト内の最初の2つを足し一つにする関数を繰り返し使いたいが上手く使えず、エラーの原因がわからず困っています。
print()で一旦、変数ansに出力するリストを格納すると上手くprintしてくれるのですが、
print()の中で直接関数の引数に関数を書いてprintしようとすると、2つ目のprintではerrorが出てしまいます。
以下のコードでIndexError: list index out of rangeが出ない様にするにはどうすれば良いでしょうか?
また、この様な問題に当たった時に、何と検索すれば解決に繋がり易いかアドバイス頂けると尚嬉しいです。

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

IndexError: list index out of range

該当のソースコード

python

1# -*- coding: utf-8 -*- 2 3N = [1, 2, 3, 4] 4 5def p1(l): 6 l_tmp = l[0] + l[1] 7 l.pop(0) 8 l[0] = l_tmp 9 return l 10 11ans = p1(p1(N)) 12 13print(ans) # ここではエラーが出ず、[6, 4]が出力される 14print(p1(p1(N))) # IndexErrorが出る

試したこと

元々、関数は複数種類あり、print(p1(p2(p4(p3(・・・)))))などとして、与えられたリストに対し関数1回使うごとにリスト先頭2要素を一つにまとめて短くしていく、もう少し複雑なコードでした。原因を特定するためにここまで簡略化したのですが、ここまで簡略化してansの中身を単に外に出してprint出来ないことに心が折れteratailに登録し初めての質問です。

補足情報(FW/ツールのバージョンなど)

python 3.7.4 を使っています

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

まず、Pythonにはミュータブル(改変可能)なオブジェクトと、イミュータブル(改変不能)なオブジェクトがあることを理解してください。

公式ドキュメントにはあまり詳しく書かれていないので、たとえば、Pythonの組み込みデータ型の分類表(ミュータブル等)などをお読みください。

Pythonの関数やメソッドでミュータブルなオブジェクトに関する処理を行う場合、関数の引数として与えられたミュータブルなオブジェクトや、ミュータブルなオブジェクトのメソッドで自分自身を改変する場合には、その関数やメソッドは改変されたミュータブルなオブジェクトを返すことは原則として行うべきではありません。

たとえば、ミュータブルなオブジェクトであるlistのsortメソッドは、listオブジェクト自身を改変するので値はNoneです。一方、引数のlistオブジェクトを改変しないsorted関数は元のlistオブジェクトではない並べ替えたlistオブジェクトを値として返します。

ご質問にあるp1は、引数であるlを改変して、そのlを値として返しているので、上記の原則に反しています。上記の原則があるのは、このような使い間違いがなるべく起きないようにするためです。

いろいろな方法がありますが、たとえば

python

1def p1(lst): 2 return [lst[0] + lst[1]] + lst[2:]

のように定義すれば、リストとリストの足し算の段階で新しいオブジェクトが作成されますので問題は起きにくくなります。

python

1>>> N = [1, 2, 3, 4] 2>>> def p1(lst): 3... return [lst[0] + lst[1]] + lst[2:] 4... 5>>> p1(N) 6[3, 3, 4] 7>>> p1(p1(N)) 8[6, 4] 9>>> p1(p1(p1(N))) 10[10]

このようにしても、

python

1>>> p1(p1(p1(p1(N)))) 2Traceback (most recent call last): 3 File "<stdin>", line 1, in <module> 4 File "<stdin>", line 2, in p1 5IndexError: list i

というエラーはでます。これは呼び出し側で対処するのが妥当です。

投稿2021/10/29 23:10

ppaul

総合スコア24666

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

takechanman

2021/10/30 04:14

回答ありがとうございます。 なるほど、p1を呼び出すたびに元のNを変更してしまっていたのですね。 それで2回目のprintではリスト長が短すぎてのIndexErrorだったと。 Nをより要素数の多いリストにしておいたり、2つのprint文の順序を入れ替えたりすれば自分でも気付けたのかと思うと悔しいです。 とても勉強になりました、ありがとうございました。
guest

0

id() でリスト Np1 関数内のリスト l のメモリアドレスを表示してみると全て同じ(同一のインスタンス)であることが判るかと思います。

python

1N = [1, 2, 3, 4] 2 3def p1(l): 4 l_tmp = l[0] + l[1] 5 l.pop(0) 6 l[0] = l_tmp 7 print(f' inside p1: {id(l)}') 8 return l 9 10print(f'global scope: {id(N)}') 11ans = p1(p1(N)) 12 13print(ans) # ここではエラーが出ず、[6, 4]が出力される 14print(N) 15print(p1(p1(N))) # IndexErrorが出る 16 17# 18global scope: 140564513653952 19 inside p1: 140564513653952 20 inside p1: 140564513653952 21[6, 4] 22[6, 4] 23 inside p1: 140564513653952 24 25Traceback (most recent call last): 26 : 27 28IndexError: list index out of range

なので、p1 にはコピーを渡す事にします。

python

1N = [1, 2, 3, 4] 2 3def p1(l): 4 l_tmp = l[0] + l[1] 5 l.pop(0) 6 l[0] = l_tmp 7 print(f' inside p1: {id(l)}') 8 return l 9 10print(f'global scope: {id(N)}') 11ans = p1(p1(N[:])) 12 13print(ans) # ここではエラーが出ず、[6, 4]が出力される 14print(N) 15print(p1(p1(N[:]))) 16 17# 18global scope: 140073137480832 19 inside p1: 140073136147520 20 inside p1: 140073136147520 21[6, 4] 22[1, 2, 3, 4] 23 inside p1: 140073136156544 24 inside p1: 140073136156544 25[6, 4]

N が多次元リストの場合には deepcopy を行う必要があります

投稿2021/10/29 18:37

編集2021/10/29 18:48
melian

総合スコア19618

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

takechanman

2021/10/30 04:20

回答ありがとうございました。 id()はエラー解決に便利そうですね!覚えておきます。 N[:]でコピーが作れるのは初めて知りました、copy.copy()を使っていたのですがこちらの方が短くて良いですね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問