Pájton három + UTF8 mától csak ezt használom!

Saját projektemet most portolom python 2-ről 3-ra. Jelszavak: csak unicode (UTF8) szöveg + telnet + terminál, magyar ékezetes input-output és feldolgozás, cross-platform win-osx-linux. Megérett a dolog a tisztán unicodera váltásra úgy érzem és ez már egy ideje eljött a python3-al.

Amúgy is mindenhol minden UTF8 képes vagy egy megfelelő helyen pipálás után az lesz. 2017 van, linux és OSX terminál unicode, windows putty 1 pipálás és UTF, a jó öreg telneten pedig csont nélkül megy keresztül az utf8 szöveg.

Az alábbi példa is mutatja, a python3 már tisztán unicode belül, minen szöveggel kapcsolatos funkciója az egyébként, jól látható a brutál szívás python2 alatt, amikor a len() függvény rossz karatker darabszámot ad vissza:


maci@OptiPlex-780:~$ python2
Python 2.7.14 (default, Sep 23 2017, 22:06:14)
[GCC 7.2.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> len('sör')
4
>>> len(u'sör')
3
>>>
maci@OptiPlex-780:~$ python3
Python 3.6.3 (default, Oct 3 2017, 21:45:48)
[GCC 7.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> len('sör')
3
>>> len(u'sör')
3
>>>

1 db unicode karakter 1-2 vagy 3 byte-ot foglalhat el. "S" betű 1 byte, magyar "ö" két byte. Ezt a python3 tudja és kezeli és jól. Python 2.x-nél még workaround kell minden egyes str(), len(), print() stb esetén, mindet reszelni kell u' vagy .encode("utf8") mókkal. Már elegem van!

Nem akarok cp852-t, ISO-8859-2-t, CP1250-et, US-ASCII-t és ezek között konvertálgatni, mától csak egy marad az pedig UTF8 és kész!


maci@OptiPlex-780:~$ locale
LANG=hu_HU.UTF-8
LANGUAGE=hu:en
LC_CTYPE="hu_HU.UTF-8"
LC_NUMERIC=hu_HU.UTF-8
LC_TIME=hu_HU.UTF-8
LC_COLLATE="hu_HU.UTF-8"
LC_MONETARY=hu_HU.UTF-8
LC_MESSAGES="hu_HU.UTF-8"
LC_PAPER=hu_HU.UTF-8
LC_NAME=hu_HU.UTF-8
LC_ADDRESS=hu_HU.UTF-8
LC_TELEPHONE=hu_HU.UTF-8
LC_MEASUREMENT=hu_HU.UTF-8
LC_IDENTIFICATION=hu_HU.UTF-8

Ennyi!

UPDATE: 1 nappal később.

Rollback lett... ezzzz a python3 naggyon durva. :-( A régi irány is rossz amikor python2 alatt megy a .encode('utf8') .decode('utf8') hegesztés, főleg hogy követni lehetetlen mikor és mikor a string type, végig kell nézni az import által betöltött kódokat is külön. Állandóan dobja az UnicodeDecodeErrort és elszáll minden. :-(

Harmadik utat választom, a legkevesebb mókkal jár, marad python2 minden, de transzparensen átengedi a cuccot magán, tehát a bejövő unicode egy idő után mint unicode kimegy az output felé.

Nem nagyon piszkálom bottal sem a stringeket, a szöveg mindig "str" típusú (azaz bytesorozat), ha van len() ott rámókolok azért encode/decode-ot.

Feladtam... majd jövőre talán, vagy akkor sem. Talán ha újraírnám 0-ról, de áh... :-)

Hozzászólások

Es meg sql folyamban nem is szotyolaltal vele ?
utf8mb4 a jelszo

Vicces dolgokat tud csinalni :D

http://karikasostor.hu - Az autentikus zajforrás.

Ez csak azért van, mert a MySQL-ben az utf8 kódolás nem a szabvány szerinti UTF-8 kódolást jelenti, hanem valami totál mást.
De hát el kell olvasni a manualt.
https://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8.html

The utf8 character set in MySQL has these characteristics:
No support for supplementary characters (BMP characters only).
A maximum of three bytes per multibyte character.

Pedig hát az UTF-8 kódolás a teljes Unicode készletet, nem csak a BMP-t tudja kódolni, és nem csak 3 byte lehet egy karakter. Mármint rendesen, a valóságban. Nem pedig a MySQL-féle szarakodásban.

A minap gondoltam, hogy írok egy kis szkriptet pythonban. Eddig a 2-est használtam, de hogy haladjak a korral, ehhez a 3-as pythont választottam. De kb 10 perc alatt rájöttem, hogy sokkal tovább tart kiguglizni, hogy mit, hogyan kell másképp csinálni, mint megírni 2-esben. Majd ha lesz időm... #miafaszernemkompatibilisvisszafele

[ Falu.me, Tárhely, Domain, VPS ]

#mia...nemkompatibilisvisszafele
Ha az lenne, akkor nem neveznék Python3-nak. Ugyanis a Python2 létrehozása után rájöttek, hogy sok minden nem logikus, másképpen kellett volna megtervezni a nyelvet (pl. az említett Unikód-támogatást). Sokat tököltek, mit tegyenek, és végül vették a bátorságot, hogy feladják a kompatibilitást.
Tekintsd a Python3-at egy teljesen más nyelvnek, és akkor nem a kompatibilitás hiánya fog feltünni, hanem a "hasonlóság":-)

--
eutlantis

... es ha arra lennék kivancsi, hogy ez az adott string (utf8: sör) egy file-ba kiirva mennyi helyet foglal el? :)

Milyen kódolásban? Először ezt kell tisztázni hozzá.

ha s egy string, akkor

len(s.encode("utf-8")) megadja azt, hogy utf8 kódolásban az adott Unicode string hány byte-ot foglal el.

Ugyanis a string.encode() bytes objektumot ad vissza (tkp egy byte tömb). Ennek a hossza lesz, amit te kérdezel.

https://docs.python.org/3/library/stdtypes.html#str.encode

Azért fontos, hogy milyen kódolásban, mert más a karakterkészlet, meg a karakterkódolás.
A stringet Unicode karakterkészletben vannak, azaz a hosszuk azt mondja meg, hány Unicode karakterből állnak (például a sör string az 3 Unicode karakter).
Az, hogy ezt a Unicode karakterek sorozatát hány byte-ban lehet lekódolni, függ a karakterkódolástól.
UTF-8-ban más lehet egy string, mint UTF-16-ban, vagy ISO-8859-2-ben. És van olyan Unicode karaktersorozat, ami nem is ábrázolható ISO-8859-2-ben például.

Ha megértjük a különbséget, hogy miért más a karakterkészlet meg a karakterkódolás (persze régen a karakterkódolás valójában egy identikus leképezés volt a karakterkészletbeli sorszám és a byte reprezentáció között, ezért a nagy keveredés a fogalmakban), akkor mindez triviálissá válik és az ember tudja, hogy mit kérdezzen.

Azért rengeteg helyen változtattak az alapértékeken. Főleg security esetén érdekes ez:
a hmac.new()-nak jelenleg van egy default "MD5" paramétere, ez deprecated, a jövőben már nem lesz default paraméter sem, azaz minden olyan kód, amiben csak annyi van, hogy hmac.new(), az el fog törni.
Az ssl értékek defaultjai is időről időre változnak, azaz egy kód máshogy viselkedik egy friss Python alatt, ha alapértelmezett értékeket használ, mint egy régebbi Python alatt.

Sokat lehet szívni ezzel, és lehet, napokig nem jössz rá, miért nem megy, aztán a végén kiderül, egy rosszul használt implicit default paraméter okoz inkompatibilitást.

Csak egy példa:
https://docs.python.org/3/whatsnew/3.6.html
distutils
The default_format attribute has been removed from distutils.command.sdist.sdist and the formats attribute defaults to ['gztar']. Although not anticipated, any code relying on the presence of default_format may need to be adapted. See bpo-27819 for more details.

Jó, ez inkább a kivétel, mint a szabály, vannak "egyszerű" dolgok, pl.:


 json.dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, cls=None, indent=None, separators=None, encoding="utf-8", default=None, sort_keys=False, **kw)

Na ha mindig ki kéne írni az összes paramétert, szerintem sokan kitépnék a hajukat. A másik meg, hogy akkor minek a default érték, ha nem lehet használni?

Hát, erre való a Parameter Object.
Vagy a builder pattern.

Nem az a baj, hogy van default értéke valaminek. A baj az, ha a kódod felhasználja, hogy MI az értéke a defaultnak.

Mondok egy példát:
legyen egy kódrészlet (függvény, interfész) specifikációja az, hogy "A bemenetként kapott string UTF-8 kódolású méretét adja vissza".
Ha te erre azt programozod le, hogy return len(s.encode()), az rossz, mert építesz arra, hogy mi a default értéke az encode paraméterének jelenleg. A helyes kód a return len(s.encode("utf-8") lenne.

Ha épít a kódod arra, hogy mi a default értéke, akkor a default értékétől függ a program viselkedése.
Míg ha a kódod specifikációja az, hogy "A bemenetként kapott string méretét adja vissza a platform alapértelmezett kódolásában", akkor valóban helyes kód a return len(s.encode());

A defaultokra igazából úgy kell rájuk tekinteni, mint a rendszer konfigurációs paramétereire. Amit ha beállítasz rendszerszinten, akkor nem kell megadni API hívásokkor, ezért kényelmes. De attól még sosem szabad elfelejteni, hogy ezek konfigurációs paraméterek valójában.


Nem az a baj, hogy van default értéke valaminek. A baj az, ha a kódod felhasználja, hogy MI az értéke a defaultnak.

Biztos velem van a baj, de én ezt a kettőt nem tudom egymástól szétválasztani. Az str.encode()-nak nem az a specifikációja, hogy a "...platform alapértelmezett kódolásában", hanem az, hogy a default az UTF8, akkor is, ha neked pándzsábira van állítva a locale. Ha ezt megváltoztatják, akkor megváltozik az API. Ennyi erővel át is nevezhetik az encodeot.

Egyébként ez a definíció:

str.encode(encoding="utf-8", errors="strict")

Akkor a te példádból az errors is hiányzik.

Vagy csak annyi, hogy szerinted az UTF8 az LC_XX-ből jön? Mert nem, ez a függvény definíciójában van.

Nem tudsz elvonatkoztatni a konkrét példától.
A példát arra hoztam, hogy mikor lehet probléma a default value: ha felhasználod egy konkrét implementációban impliciten az értékét.
Erre írtam példát, de úgy néz ki, nem jött át. Próbálj meg egy kicsit elvonatkoztatni a konkrét s.encode() példától.
Felhozhattam volna a következőt is példának:

"Írj függvényt, amely a beadott bytesorozatból HMAC-MD5-öt készít".
Python 3.4 előtt írhattad azt, hogy ezt megcsinálja a
hmac.new(), mert ilyen HMAC-et állít elő alapértelmezetten (digestmod értéke MD5).

Míg Python 3.4 óta a hmac.new() digestmod defaultja nem MD5, hanem none.

Ha eleve normálisan írod meg a kódot (specifikációnak megfelelően), akkor már Python 3.4 előtt is megfelelően paraméterezed fel a hmac.new-t, nem építesz arra, hogy mi az értéke a defaultnak.

Mint mondom: a defaultokra tessék úgy tekinteni, mint konfigurációs paraméterekre.

De hogy lehetne a defaultok értékétől elvonatkoztatni?
Ha kihasználod, hogy van default, akkor azt azért teszed, mert neked jó a default, nem azért, mert mindegy, mi az. Ha meg nem használod ki, akkor meg mintha nem is lenne default. A default érték nem konfigurálható sehol, az maga a függvény definíció része. Ha nem használnád ki, hogy mi az értéke, akkor az egész értelmét veszti.

"Ha kihasználod, hogy van default, akkor azt azért teszed, mert neked jó a default, nem azért, mert mindegy, mi az."
Nem, pont fordítva. Ha nem állítok be semmit, és hagyom a defaulton, azt azért teszem, mert mindegy, mi az.
Ha nem lenne mindegy a paraméter értéke, akkor meg kell adni, hogy mi legyen az értéke.
A kód viselkedését ne tegyük függővé a default értéktől, ez a lényeg.

"A default érték nem konfigurálható sehol, az maga a függvény definíció része. "
Mondom, nem tudsz elvonatkoztatni a konkrét esettől, és magát a problémát nézni. De hagyjuk is, nehéz absztrakciókban beszélni, ha a másik fél csak konkrétumokat ért meg.

Persze, ne mond már, hogy az str.encodenál nem számít az, hogy errors="strict", vagy errors="ignore" a default. A default értékeket nem változtatják csak úgy hasraütésre. És a konkrét hmac-os példádban, még a 3.6-os Pythonban is csak deprecation warning van, még mindig md5-ös HMAC-ot csinál, a default érték, ami konkrétan None, ezt már akár figyelmeztetésnek is lehetne nézni, hogy nem biztos, hogy használnod kell:

https://github.com/python/cpython/blob/d5c71b0c9862c4474d2700a14490dc98…

A default érték igenis az API része, mint a függvény neve, és igenis számíthatsz rá, hogy az nem fog egyik napról a másikra megváltozni (olyan módon legalábbis, amitől a programod mást fog csinálni, ha pl. egy default timeout érték megváltozik, az még nem feltétlenül gáz). Esetleg JS-ben felnőtteknél szokás ez, hogy naponta változnak a függvények definíciói?

Te nem érted, hogy a default érték az nem olyan, hogy csak úgy átírogatják, mert épp olyanjuk van. Azért default, mert általában megfelel az, amit választanak neki, és ezért nem kell kilométeres paraméterlistát írnod a függvényhíváshoz, nem azért, mert neked úgyis mindegy, mi az.

Demónak:

>>> s="Ő"
>>> s.encode("utf8")
b'\xc5\x90'
>>> s.encode("cp1250")
b'\xd5'
>>> s.encode("ibm852")
b'\x8a'
>>> s.encode("utf32")
b'\xff\xfe\x00\x00P\x01\x00\x00'
>>>
>>> "♔".encode("utf8")
b'\xe2\x99\x94'
>>> "♔".encode("cp1250")
Traceback (most recent call last):
File "", line 1, in
File "/usr/lib/python3.6/encodings/cp1250.py", line 12, in encode
return codecs.charmap_encode(input,errors,encoding_table)
UnicodeEncodeError: 'charmap' codec can't encode character '\u2654' in position 0: character maps to
>>>

--
eutlantis