Maison > développement back-end > Tutoriel Python > Explication détaillée des pièges et précautions Python couramment rencontrés en développement

Explication détaillée des pièges et précautions Python couramment rencontrés en développement

高洛峰
Libérer: 2017-03-16 17:05:39
original
1147 Les gens l'ont consulté

J'ai rencontré quelques pièges récemment lors de l'utilisation de Python, comme l'utilisation de datetime.datetime.now(), une variable objetEn tant que paramètre par défaut de la fonction , du module boucle dépendance, etc.

Enregistrez-le ici pour une future enquête et un supplément.

Évitez les objets mutables comme paramètres par défaut

Dans le processus d'utilisation des fonctions, les paramètres par défaut sont souvent impliqués. En Python, lors de l'utilisation d'objets mutables comme paramètres par défaut, des résultats inattendus peuvent survenir.

Regardez un exemple ci-dessous :

def append_item(a = 1, b = []):
    b.append(a)
    print b
    
append_item(a=1)
append_item(a=3)
append_item(a=5)
Copier après la connexion

Le résultat est :

[1]
[1, 3]
[1, 3, 5]
Copier après la connexion

Comme vous pouvez le voir sur le résultat, lorsque la fonction append_item est appelée deux fois plus tard , Paramètre de fonctionb n'est pas initialisé à [], mais conserve la valeur de l'appel de fonction précédent.

La raison pour laquelle ce résultat est obtenu est qu'en Python, la valeur par défaut d'un paramètre de fonction n'est initialisée qu'une seule fois lorsque la fonction est définie.

Regardons un exemple pour prouver cette fonctionnalité de Python :

class Test(object):  
    def init(self):  
        print("Init Test")  
          
def arg_init(a, b = Test()):  
    print(a)  
arg_init(1)  
arg_init(3)  
arg_init(5)
Copier après la connexion

Le résultat est :

Init Test
1
3
5
Copier après la connexion

Comme vous pouvez le voir à partir des résultats de cet exemple , la classe Test uniquement Elle est instanciée une fois, ce qui signifie que les paramètres par défaut n'ont rien à voir avec le nombre d'appels de fonction et ne sont initialisés qu'une seule fois lorsque la fonction est définie.

Utilisation correcte des paramètres variables par défaut

Pour les paramètres variables par défaut, nous pouvons utiliser le modèle suivant pour éviter les résultats inattendus ci-dessus :

def append_item(a = 1, b = None):
    if b is None:
        b = []
    b.append(a)
    print b
    
append_item(a=1)
append_item(a=3)
append_item(a=5)
Copier après la connexion

Le résultat est :

[1]
[3]
[5]
Copier après la connexion

Portée en Python

L'ordre de résolution de la portée de Python est Local, Englobant, Global, Intégré, ce qui signifie que l'interpréteur Python analysera les variables .


Regardez un exemple simple :

global_var = 0
def outer_func():
    outer_var = 1
    
    def inner_func():
        inner_var = 2
        
        print "global_var is :", global_var
        print "outer_var is :", outer_var
        print "inner_var is :", inner_var
        
    inner_func()
    
outer_func()
Copier après la connexion

Le résultat est :

global_var is : 0
outer_var is : 1
inner_var is : 2
Copier après la connexion

En Python, il y a une chose Il convient de noter que lors de l'attribution d'une valeur à une variable dans une portée, Python considérera la variable comme une variable locale de la portée actuelle.


Ceci est également relativement facile à comprendre. Pour le code suivant, var_func attribue une valeur à la variable num, donc num est ici une variable locale dans la portée var_func.

num = 0
def var_func():
    num = 1
    print "num is :", num
    
var_func()
Copier après la connexion

Problème 1

Cependant, lorsque nous utilisons les variables de la manière suivante, des problèmes surgiront :

num = 0
def var_func():
    print "num is :", num
    num = 1
    
var_func()
Copier après la connexion

Les résultats sont les suivants :

UnboundLocalError: local variable 'num' referenced before assignment
Copier après la connexion

La raison pour laquelle cette erreur se produit est que nous avons attribué une valeur à la variable num dans var_func, donc l'interpréteur Python pensera que num est une variable locale dans la portée var_func, mais lorsque le code est exécuté pour imprimer "num is :", num n'est toujours pas défini lors de l'exécution de l'instruction num.


Question 2

L'erreur ci-dessus est relativement évidente, et il existe également une forme d'erreur plus subtile comme suit :

li = [1, 2, 3]
def foo():
    li.append(4)
    print li
foo()
def bar():
    li +=[5]
    print li
bar()
Copier après la connexion

Le résultat du code est :

[1, 2, 3, 4]
UnboundLocalError: local variable 'li' referenced before assignment
Copier après la connexion

Dans la fonction foo, selon l'ordre d'analyse de la portée de Python, la variable globale li est utilisée dans cette fonction mais dans la fonction bar, la variable li est affectée ; une valeur, donc li sera traité comme une variable dans le cadre de bar.


Pour ce problème de la fonction barre, vous pouvez utiliser le mot clé global.

li = [1, 2, 3]
def foo():
    li.append(4)
    print li
    
foo()
def bar():
    global li
    li +=[5]
    print li
    
bar()
Copier après la connexion

ClasseAttributsMasquer

En Python, il existe des attributs de classe et des attributs d'instance. Les attributs de classe appartiennent à la classe elle-même et sont partagés par toutes les instances de classe.

Les attributs de classe sont accessibles et modifiés via le nom de la classe, et peuvent également être consultés et modifiés via l'instance de classe. Cependant, lorsqu'une instance définit un attribut portant le même nom que la classe, l'attribut de classe est masqué.


Regardez l'exemple suivant :

class Student(object):
    books = ["Python", "JavaScript", "CSS"]
    def init(self, name, age):
        self.name = name
        self.age = age
    pass
    
wilber = Student("Wilber", 27)
print "%s is %d years old" %(wilber.name, wilber.age)
print Student.books
print wilber.books
wilber.books = ["HTML", "AngularJS"]
print Student.books
print wilber.books
del wilber.books
print Student.books
print wilber.books
Copier après la connexion

Le résultat du code est le suivant Dans un premier temps, l'instance wilber peut directement. accéder à l'attribut books de la classe, mais lorsque Après que l'instance wilber ait défini un attribut d'instance nommé books, l'attribut books de l'instance wilber "masque" l'attribut books de la classe lorsque supprime l'attribut books de ; l'instance de Wilber, wilber.books correspond à l'attribut class Again books.

Wilber is 27 years old
['Python', 'JavaScript', 'CSS']
['Python', 'JavaScript', 'CSS']
['Python', 'JavaScript', 'CSS']
['HTML', 'AngularJS']
['Python', 'JavaScript', 'CSS']
['Python', 'JavaScript', 'CSS']
Copier après la connexion

Lorsque vous utilisez l' héritage dans les valeurs Python, vous devez également faire attention au masquage des attributs de classe. Pour une classe, vous pouvez afficher tous les attributs de classe via l'attribut dict de la classe.


Lors de l'accès à un attribut de classe via le nom de la classe, il recherchera d'abord l'attribut dict de la classe. Si l'attribut de classe n'est pas trouvé, il continuera la recherche. pour la classe parent. Cependant, si la sous-classe définit un attribut de classe portant le même nom que la classe parent, les attributs de classe de la sous-classe masqueront les attributs de classe de la classe parent.


Regardez un exemple :

class A(object):
    count = 1
    
class B(A):
    pass    
    
class C(A):
    pass        
    
print A.count, B.count, C.count      
B.count = 2
print A.count, B.count, C.count      
A.count = 3
print A.count, B.count, C.count     
print B.dict
print C.dict
Copier après la connexion

Les résultats sont les suivants Lorsque la classe B définit l'attribut count, l'attribut count de la classe parent. sera masqué :

1 1 1
1 2 1
3 2 3
{'count': 2, 'module': 'main', 'doc': None}
{'module': 'main', 'doc': None}
Copier après la connexion

tuple是“可变的”

在Python中,tuple是不可变对象,但是这里的不可变指的是tuple这个容器总的元素不可变(确切的说是元素的id),但是元素的值是可以改变的。

tpl = (1, 2, 3, [4, 5, 6])
print id(tpl)
print id(tpl[3])
tpl[3].extend([7, 8])
print tpl
print id(tpl)
print id(tpl[3])
Copier après la connexion

代码结果如下,对于tpl对象,它的每个元素都是不可变的,但是tpl[3]是一个list对象。也就是说,对于这个tpl对象,id(tpl[3])是不可变的,但是tpl[3]确是可变的。

36764576
38639896
(1, 2, 3, [4, 5, 6, 7, 8])
36764576
38639896
Copier après la connexion

Python的深浅拷贝

在对Python对象进行赋值的操作中,一定要注意对象的深浅拷贝,一不小心就可能踩坑了。


当使用下面的操作的时候,会产生浅拷贝的效果:


使用切片[:]操作

使用工厂函数(如list/dir/set

使用copy模块中的copy()函数

使用copy模块里面的浅拷贝函数copy():

import copy
will = ["Will", 28, ["Python", "C#", "JavaScript"]]
wilber = copy.copy(will)
print id(will)
print will
print [id(ele) for ele in will]
print id(wilber)
print wilber
print [id(ele) for ele in wilber]
will[0] = "Wilber"
will[2].append("CSS")
print id(will)
print will
print [id(ele) for ele in will]
print id(wilber)
print wilber
print [id(ele) for ele in wilber]
Copier après la connexion

使用copy模块里面的深拷贝函数deepcopy():

import copy
will = ["Will", 28, ["Python", "C#", "JavaScript"]]
wilber = copy.deepcopy(will)
print id(will)
print will
print [id(ele) for ele in will]
print id(wilber)
print wilber
print [id(ele) for ele in wilber]
will[0] = "Wilber"
will[2].append("CSS")
print id(will)
print will
print [id(ele) for ele in will]
print id(wilber)
print wilber
print [id(ele) for ele in wilber]
Copier après la connexion

模块循环依赖

在Python中使用import导入模块的时候,有的时候会产生模块循环依赖,例如下面的例子,module_x模块和module_y模块相互依赖,运行module_y.py的时候就会产生错误。

# module_x.py
import module_y
    
def inc_count():
    module_y.count += 1
    print module_y.count
    
    
# module_y.py
import module_x
count = 10
def run():
    module_x.inc_count()
    
run()
Copier après la connexion


其实,在编码的过程中就应当避免循环依赖的情况,或者代码重构的过程中消除循环依赖。

当然,上面的问题也是可以解决的,常用的解决办法就是把引用关系搞清楚,让某个模块在真正需要的时候再导入(一般放到函数里面)。

对于上面的例子,就可以把module_x.py修改为如下形式,在函数内部导入module_y:

# module_x.py
def inc_count():
    import module_y
    module_y.count += 1
Copier après la connexion


Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal