HZK
@HZK (Ritoku Sakamae)

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

16進数のint型数値を、そのままの表記で元のint型に戻すことは可能ですか

解決したいこと

16進数のint型数値を、str型にする方法は複数ありますが、開始点である元のint型に戻すことができません。必ず10進数の数値になってしまいます。

また、これと関連しますが、16進数のint型数値を要素とするリストを、そのままの状態で出力する方法も分かりません。printすると10進数の要素となってしまいます。
色々調べましたが、分かりませんでした。

計算上不要ということは何となく理解できるので、表記上の疑問ともいえます。
もし、不可能だとするならば、16進数のint型数値に戻ることは、コード上はそもそも不要という考え方でしょうか。

必要性の有無の考え方も含めて、ご教授お願い致します。

該当するソースコード

# -*- coding: utf-8 -*-

#開始点
n = 0x3421
print(type(n))
#<class 'int'>

hn = hex(n)
print(hn)
print(type(hn))
#0x3421
#<class 'str'>

result = f'{n:#x}'
print(result)
print(type(result))
#0x3421
#<class 'str'>

h_lt = [0x0, 0x16, 0x15, 0x0, 0x6, 0x7, 0x14]
print(h_lt)
#[0, 22, 21, 0, 6, 7, 20]
0

5Answer

皆さんのおっしゃるとおりなのですが、python寄りの具体的な説明をしてみますね。

16進数のint型数値という記述がありますがそういうものは存在しません。
同様に10進数のint型数値というものも存在しません。
存在するのはint型数値だけです。
値として例えば10進数数値リテラルの16を代入したint型数値変数i

i = 16

と書けます。iが指しているオブジェクトは10進数や16進数といった基数とは無関係に、数値を格納したint型オブジェクトということになり、これを文字列にする際にはじめて基数を何にするかを考える、という感じです。
基数が10進数なら"16"だし、16進数なら"10"になります(基数が2進数なら"10000"ですよね。コンピュータ的に内部格納イメージに一番近いのは2進数になります)。

int型数値str型文字列に変換する方法は複数ありますが、最も一般的によく見るのはprint()を使ったケースでしょう。

例えばprint(int(1))を実行した場合、出力文字列は1になりますが、これは組み込み型のint型整数値を引数1で実行し、値1を持つint型数値を作成し、その数値に対して、__str__()メソッドを呼び出した結果返ってきた文字列を印字する、という手順になります。仮にint型の派生クラスint2を作成し、__str__()メソッドを定義したとき、print(int2(1))がどうなるかを見てみると…

class int2(int):
    def __str__(self):
        return 'something int'

i = int(1)
print(i)
print(type(i))
# 1
# <class 'int'>

i = int2(1)
print(i)
print(type(i))
# something int
# <class '__main__.int2'>

i = int2(1) + 1
print(i)
print(type(i))
# 2
# <class 'int'>

(ちょっと比較のために余計なこともしてますが)something intという文字列が出力されていることが分かります。

つまりint型数値をstr型文字列にする場合、デフォルト(int.__str__())では10進数の文字列になるというだけなのです。16進数の文字列にするには組み込み関数のhex()を使うのが一番簡単かと思います(prefixの0xが付くけど)。

参考資料:
https://docs.python.org/ja/3/library/functions.html#print
https://docs.python.org/ja/3/library/stdtypes.html#str
https://docs.python.org/ja/3/reference/datamodel.html#object.__ str __

3Like

Comments

  1. @HZK

    Questioner

    「存在するのはint型数値だけ」
    「print()実行では、デフォルト(int.str())の呼出しで10進数の文字列になる」
    上記のように理解しました。
    例示もつけて解説して頂き、感謝しております。

int型は数値を保持するのみで、表記に関する情報は持ちません。

Pythonのint型は数値そのものを扱います。そのため、0xで始まる16進数のリテラルであっても、int型に変換されると10進数として内部的に扱われます。int型を文字列に変換した際には、どのように表現するか(10進数、16進数など)は出力時のフォーマット次第です。

1Like

Comments

  1. @HZK

    Questioner

    「int型は内部的には数値そのものであり、文字列はまさに表現の仕方によって複数存在する」よく理解できました。ありがとうございました。

16進数のint型数値を、str型にする方法は複数ありますが、開始点である元のint型に戻すことができません。必ず10進数の数値になってしまいます。

コンピューターの中では整数は 2 進数で、そこは何をどうしようと変わらず、10/16 進数うんぬんはプログラムでの書き方とかコンソールに表示するときなどにどういう形で表示するかの書式設定の問題ということは理解されてますか。

(追記: 上に書いた 2 進数というのは Python では違って、Python 独自の内部表現になるようですね。失礼しました)

文字列はそれとは違う話で、整数を文字列に変換すると 2 進数とは別の形、例えば Windows OS 内部では unicode (UTF−16) でメモリに保持されます。

1Like

Comments

  1. @HZK

    Questioner

    コンピュータにとって重要なのは2進数のみ。文字列は当然のこととして、16進数ですら人間の便益のために存在するのだということを、より深く理解できました。ありがとうございました。

興味深い話題なので、書いてみます。
基本は外の方の意見と同じです。

プログラミング言語には「リテラル」というデータの表現方法があります。「Hello」という文字列を表すリテラルは"Hello"だし、10進数の12を表すリテラルは12で、Bool値の真と偽からなるリストのリテラルは[True, False]です。これらのリテラルは、言語の仕様の一部であり、プログラムの記述が楽になうりょうに、直感的にそうだと思えるような表現になっていたり簡単な仕組みを持っていたりします。
また、リテラルによる表現は一意ではなく、同じオブジェクトを複数の方法で表わすことができる場合もあります。"world"'world'は同じ文字列を表わし、0x10と'16`は同じ数値を表わします。

ただ、ここで気をつけなければならないことは、リテラルがそのデータそのものを表わしているわけではないということです。プログラムの中にあるリテラルは、コンパイラーやインタープリタに読み込まれて、プログラムの内部表現に変換されます。pythonのデータはオブジェクト指向に基づいているので、メソッドや属性を持ったデータの塊になっています。
表面的な話をすれば、たとえば[True, False]というリストのリテラルには、「[]」や「,」が含まれていますが、リストの要素にはこれらのものは含まれません。文字列の「"」などについても同様です。
整数については、基数を変えた表現が使えます。単に数値を書くと基数は10(10進数)とみなされます。他の基数では、2, 8, 16が使え、それぞれ、0b10000o100x8とあらわせますが、すべて10進数の「8」と同じもので、同じオブジェクトが生成されます。また、生成されたオブジェクトには、どのようなリテラルから生成されたものなのかという情報は含まれていません。

ということで、質問にある、「16進数のint型数値」とか「10進数のint型数値」というものは存在しないといくとおちぃなります。

内部のオブジェクトを出力するときにも、いろいろな仕組みが働いています。
変数には、リテラルそのものではなくそこから作られたオブジェクトが入っているわけで、そのオブジェクトをprintで出力しようとしたときにどのようになるかについては、オブジェクトの仕様によって異なります。そのオブジェクトがリストであれば、要素をカンマで区切り、前後を「[]」でくくって出力します。 これはリテラルの表現と同じです。文字列であれば、構成する文字をすべてそのまま出力します。これはリテラルの表現とは異なります。

整数の場合は、10進数として表示します。
「16進数のint型数値」が「10進数のint型数値」になってしまったように見えるのはこのためでしょう。

1Like

Comments

  1. @HZK

    Questioner

    「0x10, 16も同一のint型オブジェクトで、リテラルが違うだけ」
    参考書では読み飛ばしてしまう説明も、この場で解説して頂くと理解が深まるのには驚くばかりです。ありがとうございました。

>>> n = 0x3421  # 16進数整数リテラル、int型オブジェクト、内部はバイナリ(2進数)データ
>>> print(n)  # 10進数表示
13345
>>> print(hex(n))  # 16進数表示
0x3421
>>> s = hex(n)  # 16進数文字列に変換
>>> type(s)
<class'str'>
>>> print(s)  # 16進数文字列表示
0x3421
>>> i = int(s, 16)  # 16進数文字列をint型整数に変換
>>> print(i)
13345
>>> print(hex(i))
0x3421
>>> h_lt = [0x0, 0x16, 0x15, 0x0, 0x6, 0x7, 0x14]
>>> print(f'[{", ".join(map(hex, h_lt))}]')  # 要素を16進数文字列変換にマップして連結
[0x0, 0x16, 0x15, 0x0, 0x6, 0x7, 0x14]
0Like

Your answer might help someone💌