Siirtyminen Python 3: HealthifyMe-kokemukseen.

( 18. marraskuuta 2020)

Hei!, Tämä blogi on kuinka tehdä vanhaan monoliittiprojektiin Python 3 -siirto ilman seisokkeja. Meillä on tätä varten kaksi osaa.

Ensimmäinen blogi: Tässä blogissa on tietoja siitä, miksi sinun pitäisi siirtyä Python 3: een, ja harvinaisten tapausten ero Python 2: ssa ja Python 3: ssa ja Yhteensopivat ratkaisut niille .

(Toinen blogi): Kuinka HealthifyMe siirtyi Python 3: een ilman seisokkeja jatkuvan kehityksen kanssa.

Johdanto

Python on HealthifyMen ensisijainen koodauskieli. Käytämme Python 3: ta kaikissa uusissa projekteissa, mutta perintöprojektimme oli edelleen käynnissä Python 2.7: n ja Djangon (1.11) kanssa. Monoliittiprojektimme on 7 vuotta vanha, ja siinä on yli 2 miljoonaa riviä python-koodia. Joitakin syitä, miksi siirtyimme python 3: een:

  1. Python 2: n tuki putosi: Tammikuun 2020 jälkeen Python 2: n tukea ei tarjota.
  2. Python 3: n hyväksyminen: As Suurin osa yrityksistä avoimen lähdekoodin projektissa on jo ottanut käyttöön Python 3: n. Uudet kirjastot, työkalut, moduulit, kehykset kirjoitetaan Python 3: een.
    Myös nykyinen avoimen lähdekoodin projekti siirtyi ja uusia ominaisuuksia, korjauksia, tietoturvan parantamista ovat tulossa Python 3: ssa.
  3. Ohjelmistojen suojaus: Ohjelmistojen tietoturvan varmistaminen on lakisääteinen vaatimus varsinkin kun olet tekemisissä henkilötietojen kanssa GDPR-alueella. Ohjelmistosi pitäminen ajan tasalla on ymmärrettävän hyvin tietoturvan parhaiden käytäntöjen joukossa, ja vanhentunut Python-tulkki olisi kaikki taattu näy punaisena lippuna turvatarkastuksen aikana. Suojaustestaustyökalut, kuten Black Duck, ovat ilmoittaneet monista haavoittuvuuksista, hyväksikäytöistä ja turvallisuusongelmista Python 2: ssa. Ja suurin osa niistä on korjattu uusimmissa Python 3 -versioissa (3.7.5, 3.8.0).
  4. Suorituskyky ja uudet ominaisuudet : Python 3: n suorituskyky on parempi kuin python 2. Uusi python 3 -ohjelmaa käyttävä ohjelmistotuote on raportoinut 12\%: n suorittimen suorituskyvyn kasvun ja 30\%: n parannuksen muistiresurssien käytössä .
    myös python 3 antaa meille:
    * natiivi asynkroninen ohjelmointi.
    * -tyyppisiä huomautuksia
    voit käyttää parantamaan staattisen koodin analyysiä ja yleistä käytettävyyttä.
    * ketjutetut poikkeukset , jotka ovat erityisen hyödyllisiä virheenkorjauksessa.
    * muita hyödyllisiä ominaisuuksia , jotka tekevät koodaamisesta Pythonissa paljon tehokkaampaa.

Tätä luetteloa jatketaan, ja se varmasti kasvaa jokaisen uuden Python 3 -julkaisun kanssa.

Nämä ovat muutamia syitä, miksi voimme siirtyä python 3: een. Meillä on noin 12–15 kehittäjien taustaryhmää. Tämä on perusprojektimme, joka sisältää päivittäin 7–10 koontiversiota, vikakorjauksia, parannuksia, tietoturvakorjauksia, uusien ominaisuuksien kehittämistä jne. Suurin haasteemme ei ollut pysäyttää nykyistä kehitysprosessia. Meidän oli varmistettava, että projektimme on yhteensopiva Python 3.X: n kanssa rikkomatta yhteensopivuutta Python 2.X: n kanssa. Siirtymistä johti yksi kehittäjä (tietysti muiden kehittäjien avulla).

Tässä artikkelissa yritämme havainnollistaa kaikkia toteutettuja vaiheita, kohtaamiamme ongelmia ja muutamia lisätietoja .

Ero python 2: n ja python 3: n välillä.

Löydämme yleisen eron täältä:

https://docs.python.org/ 3 / whatsnew / 3.0.html
https://sebastianraschka.com/Articles/2014\_python\_2\_3\_key\_diff.html
https://jaxenter.com/differences-python-2-3-148432.html
https: // python-future.org/compatible\_idioms.html

Seuraavassa kuvataan lyhyesti joitain harvinaisia ​​ja reunatapauksia kohtaavat, kun aloitimme siirron.

Tietotyyppien vertailu:

1. Python 2: ssa integer: n ja none: n vertaaminen toimii siten, että none katsotaan vähemmän kuin kokonaisluku, jopa negatiiviset.Voit myös verrata none -sivustoa string, string kanssa int.
Python 3: ssa ei sallita erilaisten tietotyyppien vertailua.
Suurin osa kehittäjistä tietää tämän, mutta edessämme oli tapaustapa, jossa NotImplementedType verrattiin ryhmään int ja tämä ei toimi python 3: ssa.

Koodinpätkä:

class Base(object):
PHONE\_NO\_SIZE\_LIMIT = NotImplementedbase = Base()
if base.PHONE\_NO\_SIZE\_LIMIT > 10:
print("Pass correct phone number")
else:
print("Valid phone number")

Jos tallennamme tämän phone\_number\_validation.py -palvelun kanssa ja suorita koodi:

# Python 2
[email protected] ~ \% python phone\_number\_validation.py
Pass correct phone number# Python 3
[email protected] ~ \% python3.7 phone\_number\_validation.py
Traceback (most recent call last):
File "phone\_number\_validation.py", line 4, in
if base.PHONE\_NO\_SIZE\_LIMIT > 10:
TypeError: ">" not supported between instances of "NotImplementedType" and "int"

Yhteensopiva Ratkaisu:
Meidän on tarkistettava, onko base.PHONE\_NO\_SIZE\_LIMIT toteutettu vai ei, ellei niin, meidän on käsiteltävä sitä. Kuten:

if isinstance(base.PHONE\_NO\_SIZE\_LIMIT, type(NotImplemented)):
# Have logic here, also exit/return here.
print("Phone size is not implemented")if base.PHONE\_NO\_SIZE\_LIMIT > 10:
print("Pass correct phone number")
else:
print("Valid phone number")

2. Minimi-, maksimi-matemaattiset funktiot:
Vertailun kallistuksen työ int none, intstr, none muotoon str python 3: ssa, joten myös matematiikkafunktio min ja Max ovat muuttuneet python 3: ssa.

# Python 2 
>>> max([1, None, 2])
2
>>> max([1, None, 2, "abc"])
"abc"
>>> min([1, None, 2, "abc"])
None # Python 3
>>> max([1, None, 2])
Traceback (most recent call last):
File "", line 1, in
TypeError: ">" not supported between instances of "NoneType" and "int" >>> max([1, 2, "abc", None])
Traceback (most recent call last):
File "", line 1, in TypeError: ">" not supported between instances of "str" and "int"

Yhteensopiva ratkaisu:
1. Luettelossa on oltava yhden tyyppisiä tietoja joko string tai int
2. Yhdellä tyypillä, jos none on olemassa, meillä voi olla oma tapa käsitellä tätä.

def py2max(input\_list):
"""Get the maximum item from list."""
if not input\_list:
raise ValueError("List should not be empty")
formated\_input\_list = [rec for rec in input\_list if rec is not None]
return max(formated\_input\_list) if formated\_input\_list else None

Hex-koodaus / dekoodaus

Kun koodaamme merkkijonoa python 2: ssa, noudatamme tätä .encode(‘hex’) Tämä ei toimi Python 3: ssa

# ENCODING# Python 2
>>> "msg\_to\_be\_encoded".encode("hex")
"6d73675f746f5f62655f656e636f646564"# Python 3
>>> "msg\_to\_be\_encoded".encode("hex")
Traceback (most recent call last):
File "", line 1, in
LookupError: "hex" is not a text encoding; use codecs.encode() to handle arbitrary codecs# DECODING# Python 2
>>> "6d73675f746f5f62655f656e636f646564".decode("hex") "msg\_to\_be\_encoded"# Python 3
>>> b"6d73675f746f5f62655f656e636f646564".decode("hex")
Traceback (most recent call last):
File "", line 1, in
LookupError: "hex" is not a text encoding; use codecs.decode() to handle arbitrary codecs# Also look at exception here in python 2 and python 3.# Python 3>>> b"6d73675f746f5f62655f656e636f646564".decode("hex")
Traceback (most recent call last):
File "", line 1, in
LookupError: "hex" is not a text encoding; use codecs.decode() to handle arbitrary codecs# Python 2>>> "6d73675f746f5f62655f656e636f64656".decode("hex")
Traceback (most recent call last):
File "", line 1, in
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/encodings/hex\_codec.py", line 42, in hex\_decode
output = binascii.a2b\_hex(input)
TypeError: Odd-length string

Yhteensopiva ratkaisu:
Meidän tulisi käyttää codecs Python 3: lle ja 2: lle. Python 3: n tuloissa ja lähdöissä molemmat ovat byte tietotyyppi.

# Python 2
>>> import codecs
>>> message = "msg\_to\_be\_encoded"
>>> codecs.encode(message.encode(), "hex")
"6d73675f746f5f62655f656e636f646564"# Python 3
>>> message = "msg\_to\_be\_encoded"
>>> codecs.encode(message.encode(), "hex")
b"6d73675f746f5f62655f656e636f646564"

Merkkijonon isot kirjaimet:

string.uppercase ei toimi python 3: ssa.

# Python 2 
>>> import string
>>> string.uppercase
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"# Python 3
>>> import string
>>> string.uppercase
Traceback (most recent call last):
File "", line 1, in
AttributeError: module "string" has no attribute "uppercase"

yhteensopiva ratkaisu:
Käytä ascii\_uppercase

# Python 2
>>> import string
>>> string.ascii\_uppercase
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"# Python 3
>>> import string
>>> string.ascii\_uppercase
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"

hasattr ():

hasattr() esittää valtavan reunatapauksen kirjoitettaessa Python 2 ja 3 yhteensopiva koodi. hasattr() tarkistaa määritteen olemassaolon yrittämällä sitä hakea.

Python 2

hasattr ( object , nimi )
Argumentit ovat objekti ja merkkijono. Tulos on tosi, jos merkkijono on objektin jonkin määritteen nimi, väärä, jos ei. (Tämä toteutetaan kutsumalla getattr (objekti, nimi) ja katsomalla, aiheuttaako se poikkeuksen vai ei.)

Python 3

hasattr ( objekti , nimi ) Argumentit ovat objekti ja merkkijono. Tulos on tosi, jos merkkijono on objektin jonkin määritteen nimi, väärä, jos ei. (Tämä toteutetaan kutsumalla getattr (objekti, nimi) ja katsomalla, aiheuttaako se AttributeError vai ei.)

Löydät lisätietoja täältä: (https://medium.com/@k.wahome/python-2-vs-3-hasattr-behaviour-f1bed48b068)

Esimerkkikoodi

class Foo(dict): 
def \_\_init\_\_(self):
super(Foo, self).\_\_init\_\_()
self.example\_dict = {}

def \_\_getitem\_\_(self, key):
try:
return super(Foo, self).\_\_getitem\_\_(key)
except KeyError:
return self.example\_dict[key] def \_\_getattr\_\_(self, key):
return self[key]foo = Foo()
if hasattr(foo, "not\_present\_key"):
pass
else:
print("Not Found")

Tallenna yllä oleva katkelma hasattr\_test.py

# Python 2[email protected] ~ \% python hasattr\_test.py 
Not Found# Python 3[email protected] ~ \% python3.7 hasattr\_test.py
Traceback (most recent call last):
File "hasattr\_test.py", line 8, in \_\_getitem\_\_
return super(Foo, self).\_\_getitem\_\_(key)
KeyError: "not\_present\_key"During handling of the above exception, another exception occurred:Traceback (most recent call last):
File "hasattr\_test.py", line 17, in
if hasattr(foo, "not\_present\_key"):
File "hasattr\_test.py", line 13, in \_\_getattr\_\_
return self[key]
File "hasattr\_test.py", line 10, in \_\_getitem\_\_
return self.example\_dict[key]
KeyError: "not\_present\_key"

Yhteensopiva ratkaisu:
Jotta koodin yhteensopivuus Python 2: ssa ja Python 3: ssa, meidän on muutettava \_\_getattr\_\_ -funktiota kuten alla.

def \_\_getattr\_\_(self, key):
try:
return self[key]
except KeyError:
raise AttributeError

Sanelu järjestetään python 3: ssa:

Python 3.6+: sta eteenpäin sanakirja on nyt oletusarvo lisäys järjestetty .

# Python 2
>>> sample\_dict = {}
>>> sample\_dict["a"] = 1
>>> sample\_dict["b"] = 2
>>> sample\_dict["c"] = 3
>>> sample\_dict["d"] = 4
>>> sample\_dict
{"a": 1, "c": 3, "b": 2, "d": 4}# Python 3
>>> sample\_dict = {}
>>> sample\_dict["a"] = 1
>>> sample\_dict["b"] = 2
>>> sample\_dict["c"] = 3
>>> sample\_dict["d"] = 4
>>> sample\_dict
{"a": 1, "b": 2, "c": 3, "d": 4}

yhteensopiva koodi:
Ihannetapauksessa tämä ei saa rikkoa sovelluskoodia, koska se muuttui järjestämättömäksi järjestetyksi dikiksi. Jos silti, tarvitsemme saman tuloksen molemmissa python-versioissa (järjestys on tärkeä, testitapaus epäonnistuu), meidän on käytettävä OrderedDict pitämään molemmat kielilähdöt samana.

Hajautus:

Python 2: ssa tulo voidaan kirjoittaa unicode ja str, mutta Python 3: ssa se tarvitsee bytes

# Python 2
>>> import hashlib
>>> message = "healthify"
>>> hashlib.sha512(message).hexdigest().lower()
"f910c1fa68087a546512ac3b175c99ee7eba21360fa4e579c2aed649c7e4a43466c56bceedcd60d783bc6e7d069a16f0b9c67140d6c129d2a1898af8cfb62719"# Python 3
>>> message = "healthify"
>>> hashlib.sha512(message).hexdigest().lower()
Traceback (most recent call last):
File "", line 1, in
TypeError: Unicode-objects must be encoded before hashing

Yhteensopiva ratkaisu:
Python 3 tarvitsee bytes syötteenä, jossa python 2 toimii tyyppien unicode ja str molempien kanssa.

# Python 2
>>> import hashlib
>>> message = "healthify"
>>> hashlib.sha512(message.encode("utf-8")).hexdigest().lower()
"f910c1fa68087a546512ac3b175c99ee7eba21360fa4e579c2aed649c7e4a43466c56bceedcd60d783bc6e7d069a16f0b9c67140d6c129d2a1898af8cfb62719"# Python 3
>>> message = "healthify"
>>> hashlib.sha512(message.encode("utf-8")).hexdigest().lower()
"f910c1fa68087a546512ac3b175c99ee7eba21360fa4e579c2aed649c7e4a43466c56bceedcd60d783bc6e7d069a16f0b9c67140d6c129d2a1898af8cfb62719"

\_\_div\_\_ operaattorin ylikuormitus:

Python 3: ssa \_\_div\_\_ -operaattoria ei näytä olevan olemassa, koska se korvattiin kokonaan \_\_truediv\_\_ -palvelimella.

class item:
fats = 0.0

def \_\_div\_\_(self, other):
self.fats = self.fats / otherit = item()
it.fats = 34.0
it / 3
print(it.fats)# python 2 output
11.3333333333# Python 3 output
Traceback (most recent call last):
File "div\_overloading.py", line 16, in
print(AB / 3)
TypeError: unsupported operand type(s) for /: "Vector2" and "int"

Yhteensopiva ratkaisu:
Python 3.x: ssä meidän on ylikuormitettava \_\_truediv\_\_ -operaattoreita, ei \_\_div\_\_ operaattori. Jotta koodi olisi yhteensopiva, meidän on pidettävä molemmat menetelmät, kuten:

class item:
fats = 0.0 def \_\_div\_\_(self, other):
self.fats = self.fats / other

def \_\_truediv\_\_(self, other):
self.fats = self.fats / otherit = item()
it.fats = 34.0
it / 3
print(it.fats)# python 2 output
11.3333333333# Python 3 output
11.333333333333334

Base64-koodaus:

Teemme base64-koodauksen käyttämällä base64.b64encode(). Python 2: ssa voimme välittää syötteenä unicode tai str. Mutta python 3: ssa se tarvitsee syötteenä bytes.

# Python 2
>>> from base64 import b64encode
>>> b64encode("man")
"bWFu"
>>> b64encode(u"man")
"bWFu"# Python 3
>>> from base64 import b64encode
>>> b64encode("man")
Traceback (most recent call last):
File "", line 1, in
File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/base64.py", line 58, in b64encode
encoded = binascii.b2a\_base64(s, newline=False)
TypeError: a bytes-like object is required, not "str"

Yhteensopiva ratkaisu:
Meillä voi olla oma menetelmä base64-koodaukselle, ja sillä voi olla input string ja bytes molemmat.

import base64
import six
def base64ify(bytes\_or\_str):
if six.PY3 and isinstance(bytes\_or\_str, str):
input\_bytes = bytes\_or\_str.encode("utf8")
else:
input\_bytes = bytes\_or\_str
try:
output\_bytes = base64.b64encode(input\_bytes)
except (UnicodeEncodeError, TypeError):
# This happens when the input message has
# non-ascii encodable characters in an unicode string
# `"`(ascii encodable) vs `’`(non-ascii encodable)
# In this case, we first need to encode it to utf-8
# and then do the base64 encoding
output\_bytes = base64.b64encode(input\_bytes.encode("utf-8"))
if six.PY3:
return output\_bytes.decode("ascii")
else:
return output\_bytes

Sisäänrakennettu pyöreä menetelmä:

Python2 : Pyöristys suoritetaan poispäin kohdasta (eli esimerkiksi pyöreä (0,5) on 1,0 ja pyöreä (-0,5) -1,0)
Python 3 : Pyöristys tehdään parillisen valinnan suuntaan (eli esimerkiksi sekä pyöreät (0,5) että pyöreät (-0,5) ovat 0 ja pyöreät (1,5) ovat 2).

# Python 2
>>> round(15.5)
16.0
>>> round(16.5)
17.0# Python 3
>>> round(15.5)
16
>>> round(16.5)
16

yhteensopiva ratkaisu:
Loimme o oma pyöreä menetelmäsi, joka toimii samalla tavalla kuin Python 2 -kierros myös python 3: ssa.

def py2\_round(x, d=0):
"""Round same as PY2 in PY3."""
p = 10 ** d
if x >= 0:
return float(math.floor((x * p) + 0.5)) / p
else:
return float(math.ceil((x * p) - 0.5)) / p

struct. paketin syötetyyppi:

syötetyyppi on str, Python 3: n pitäisi olla byte

# Python 2
>>> import struct
>>> import struct
>>> string = "blab"
>>> s = struct.Struct(b"4s")
>>> packed\_data = s.pack(string)# Python 3
>>> import struct
>>> string = "blab"
>>> s = struct.Struct(b"4s")
>>> packed\_data = s.pack(string)
Traceback (most recent call last):
File "", line 1, in
struct.error: argument for "s" must be a bytes object

Yhteensopiva ratkaisu:
encode tulo .

luettelomuuttuja scope muutokset:

Python 3: ssa luettelon ymmärtämismuuttuja käyttää sulkeutuvaa aluetta tarkoittavat, että et voi käyttää suunnitelmamuuttujaa toiminnon ulkopuolella, tämä oli ei Python 2: ssa.

# Python 2
>>> def two\_or\_three():
... x = 3
... [0 for x in range(3)]
... return x
...
>>> two\_or\_three()
2>>> def two\_or\_three\_with\_method():
... def print\_number():
... print(x)
... [0 for x in range(3)]
... print\_number()
...
>>> two\_or\_three\_with\_method()
2# Python 3
>>> def two\_or\_three():
... x = 3
... [0 for x in range(3)]
... return x
...
>>> two\_or\_three()
3>>> def two\_or\_three\_with\_method():
... def print\_number():
... print(x)
... [0 for x in range(3)]
... print\_number()
...
>>> two\_or\_three\_with\_method()
Traceback (most recent call last):
File "", line 1, in
File "", line 5, in two\_or\_three
File "", line 3, in print\_number
NameError: name "x" is not defined

Yhteensopiva ratkaisu:
Meidän tulisi välttää tällaisia ​​tapauksia. Toista menetelmää (two\_or\_three\_with\_method) varten meidän on välitettävä x-arvo argumenttina.

math.floor ja math.ceil-palautustietotyyppi muuttui:

Python 2: ssa lattia ja katto palauttavat kelluvan tietotyypin, mutta python 3: ssa palauttaa int-tietotyypin.

# Python 2
>>> from math import floor,ceil
>>> floor(4.345)
4.0
>>> ceil(4.345)
5.0# Python 3
>>> from math import floor,ceil
>>> floor(4.345)
4
>>> ceil(4.345)
5

Yhteensopiva ratkaisu:
Voimme tee ulostulo kellukkeena python 3: ssa. Se ei vaikuta python 2: een, float(floor(4.345))

python 2 peitattu objekti python 3: ksi:

Mainitaksemme, teemme koodista yhteensopivan molempien python-versioiden suorittamiseksi. kohtasimme ongelman, kun kohdetta peitattiin Python 2: ssa, mutta emme pysty poistamaan valintaa python 3: ssa. Tämä voi tapahtua myös Redis Pickled -välimuistissa olevien kohteiden kohdalla.

pickle.load(), oletuksena on yrittää purkaa kaikki merkkijonodata ASCII: ksi, ja dekoodaus epäonnistuu. Katso

pickle.load() ohjeet :

Valinnaiset avainsana-argumentit ovat fix\_imports , koodaus ja virheet , joita käytetään ohjaamaan Python 2: n tuottaman suolakurkku-virran yhteensopivuustukea. pickle yrittää kartoittaa vanhat Python 2 -nimet uusiin nimiin, joita käytetään Python 3: ssa. Koodaus ja virheet kertovat suolakurkulle kuinka purkaa Pythonin peittaamat 8-bittiset merkkijonotapaukset 2; oletusarvoisesti ”ASCII” ja ”tiukka”. koodaus voi olla tavua lukemaan nämä 8-bittiset merkkijonotapaukset tavuobjekteina.

https : // pinoverflow.com / questions / 28218466 / unpickling-a-python-2-object-with-python-3

Yhteensopiva ratkaisu:
Voimme poistaa alla olevien menetelmien avulla esineiden poiminnan.

def unpickling\_py2\_to\_py3(pickled\_value):
"""Unpickling python 2 pickled in to python 3."""
if isPY3():
try:
value = pickle.loads(pickled\_value)
except UnicodeDecodeError:
value = pickle.loads(pickled\_value, encoding="latin1")
else:
value = pickle.loads(pickled\_value)
return value

Kolmannen osapuolen kirjaston korjaukset:

Projektissamme käytämme paljon kolmansien osapuolten paketteja, samalla kun päivitämme niitä, kohtasimme joitain reunatapauksia . Voit ohittaa tämän, jos et käytä mitään niistä.

  1. Django :
    a. Django-siirtotiedostot
    Kun suoritamme Django makemigrations python 3: ssa, näimme uusia siirtotiedostoja. mutta sama ei tapahtunut python 2: lla. Tähän voi olla useita syitä.

b etuliite: Makemigrationissa suurin osa uusista tiedostoista luodaan ilman merkkijonojen arvoja b prefix, koska kaikki mallissasi ja kentissäsi käytettävät merkkijonot (esim. ” verbose\_name”, ” related\_name” jne.), täytyy olla johdonmukaisesti joko tavujonoja tai teksti (unicode) merkkijonoja sekä Python 2: ssa että 3: ssa.

Yhteensopiva ratkaisu: Helpoin tapa saavuttaa yksi siirto uutta siirtoa varten lisää from \_\_future\_\_ import unicode\_literal kaikkiin mallitiedostoihin. Joko suoritamme olemassa oleville siirtotiedostoille makemigration ja sen pitäisi tapahtua vain kerran, tai voimme poistaa b prefix olemassa olevista siirtotiedostoista.

Valintakenttä: Mallissa käytämme dict.items (). Kuten tiedämme nyt, että -dikti järjestetään python 3: ssa , joten dict.items (): n palauttamat arvot eroavat toisistaan Python 2 ja Python 3.

Yhteensopiva ratkaisu: Jotta molemmat olisivat yhteensopivia, lajittelimme (dict. items ()) ja luotu siirtotiedosto, joka on nyt yhteensopiva molempien python-versioiden kanssa.

b. “ Object” -näyttö hallintakonsolissa
Hallintakonsolin python 3: ssa näemme Object-kentän arvon merkkijonon sijaan. jos tapahtui, koska malliluokassamme on menetelmä.

def \_\_unicode\_\_(self):
return "MyModel: {}".format(self.name)

Meillä voi olla str-menetelmä \_\_str\_\_ ja se toimii sekä Python 2: lla että Python 3: lla. Mutta se epäonnistuu, jos str-versiossa on muita kuin ASCII-merkkejä.

Yhteensopiva Ratkaisu: Sain ratkaisun täältä , lisättiin @python\_2\_unicode\_compatible sisustaja malleille ja muokattu \_\_unicode\_\_ muotoon \_\_str\_\_.

c . Django-kyselyobjektin leikkaaminen
Django-kyselyobjektilla on viipalointikyky hakea tietueita. Django-versiolle (1.11), python 2: lle, se tukee int: n ja str: n viipalointia. Python 3: ssa se tukee vain viipalointia intin kautta.

# Python 2
>>> from food import models
>>> foods = models.foods.objects.all()
>>> foods[1:2] # int slicing
]>
>>> foods["1":"2"] # string slicing
]># Python 3
In [2]: from food import models
In [3]: foods = models.Foods.objects.all()
In [4]: foods["1":"2"]
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
in
----> 1 foods["1":"2"]~/Venvs/py3\_venv/lib/python3.7/site-packages/django/db/models/query.py in \_\_getitem\_\_(self, k)
265 raise TypeError
266 assert ((not isinstance(k, slice) and (k >= 0)) or
--> 267 (isinstance(k, slice) and (k.start is None or k.start >= 0) and
268 (k.stop is None or k.stop >= 0))), \
269 "Negative indexing is not supported."TypeError: ">=" not supported between instances of "str" and "int"In [5]: foods[1:2]
Out[5]: ]>

Yhteensopiva ratkaisu: Vältä merkkijonojen viipalointia, joka ei silti ole hyvä tapa.

2. Redis : Redis on yleinen käytetty python-paketti. redis-py 3.0 esittelee monia uusia ominaisuuksia, mutta vaati useita taaksepäin yhteensopimattomia muutoksia prosessissa.

Yhteensopiva ratkaisu:
https://pypi.org/project/redis/ täältä voimme selvittää muutokset ja tehdä niistä yhteensopivia koodi. Teimme omat Redis-menetelmät, jotka ovat yhteensopivia molempien Redis-versioiden kanssa. Tykkää

import six
def redis\_zadd(redis\_connection, key, **values):
"""Redis method zadd for python 2 and python 3 compatibility."""
if six.PY3:
redis\_connection.zadd(key, values)
else:
redis\_connection.zadd(key, **values)def redis\_zincrby(redis\_connection, key, value, score):
"""Redis method zincrby for python 2 and python 3 compatibility."""
if six.PY3:
redis\_connection.zincrby(key, score, value)
else:
redis\_connection.zincrby(key, value, score)

3. django-cacheops : Välimuistit ovat tyylikäs sovellus, joka tukee automaattista tai manuaalista kyselyryhmän välimuistia ja automaattista tapahtumapohjaista mitätöintiä. Siellä on gotcha, kun taas Django-välimuistit tallentavat arvot Redisille, mikä tekee niistä suolakurkkua.

Python 2: ssa on 3 erilaista protokollaa (0, 1, 2) ja oletus on 0. python 3: ssa on 5 erilaista protokollaa (0, 1, 2, 3, 4) ja oletusarvo on 3.

Pickle käyttää oletusarvoista suolakurkkuprotokollaa tietojen tyhjentämiseen. python 3, jos teemme suolakurkkuobjektin ja haluamme poistaa peittauksen Python 2: ssa, ei toimi, koska suolakurkkuprotokolla 3 ei ole käytettävissä python 2: ssa. > Yhteensopiva ratkaisu:
Voimme määrittää protokollaparametrin kutsuttaessa pickle.dump.
django-välimuistilla ei ole mahdollisuus tarjota suolakurkkuprotokolla. Käytimme apinan korjausta tämän selvittämiseen.

import cacheops
from cacheops.cross import pickle
@cacheops.redis.handle\_connection\_failure
def
\_custom\_cacheops\_redis\_set(self, cache\_key, data, timeout=None):
pickled\_data = pickle.dumps(data, 2) # Protocol 2 is valid in both Python version.
if timeout is not None:
self.conn.setex(cache\_key, timeout, pickled\_data)
else:
self.conn.set(cache\_key, pickled\_data)cacheops.RedisCache.set = \_custom\_cacheops\_redis\_set

Kuten edellä mainittiin, miten python 2 poistetaan peitattu esine python 3: een . Haluamme saada dataa python 3: een, voimme kohdata UnicodeDecodeError-haaran eri python-versioissa tapahtuvan poiminnan vuoksi.
tämä myös lajitellaan korjaustiedostolla

import six
from cacheops.simple import CacheMiss
if six.PY3:
import pickle
else:
import cPickle as pickledef unpickling\_py2\_to\_py3(pickled\_value):
"""Unpickling python 2 pickled in to python 3."""
if six.PY3:
try:
value = pickle.loads(pickled\_value)
except UnicodeDecodeError:
value = pickle.loads(pickled\_value, encoding="latin1")
else:
value = pickle.loads(pickled\_value)
return valuedef
\_custom\_cacheops\_redis\_get(self, cache\_key):
data = self.conn.get(cache\_key)
if data is None:
raise CacheMiss
return unpickling\_py2\_to\_py3(data)cacheops.RedisCache.get = \_custom\_cacheops\_redis\_get

4. django-redis-cache : meillä on menetelmä näppäinten poistamiseksi kuvion perusteella. Python 2: ssa käytämme versiota

1.6.5 avaimen haku / poisto tapahtui ilman tarkistusta, mutta python 3: lle päivitimme version muotoon

2.1 missä kuvahaku tapahtuu Redis-skannauksen avulla, mikä tekee siitä niin hitaan. Tämä aiheutti ongelman. Git-keskittimen ongelma tätä varten .

Yhteensopiva ratkaisu:
Lajittelimme ongelman käyttämällä vanhaa tapaa poistaa kuviota. sen sijaan, että soittaisimme cache.delete\_pattern(pattern), teemme

pattern = cache.make\_key(pattern)
keys = cache.master\_client.keys(pattern)
if len(keys):
cache.master\_client.delete(*keys)

Mitä seuraavaksi

Sisään (toinen osa ) tämän blogin , jossa tutkitaan, miten siirtyä python 3: een ilman seisokkeja jatkuvan kehityksen kanssa.

Vastaa

Sähköpostiosoitettasi ei julkaista. Pakolliset kentät on merkitty *