Maison >développement back-end >Tutoriel Python >Explication détaillée des pièges et précautions Python couramment rencontrés en développement
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.
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)
Le résultat est :
[1] [1, 3] [1, 3, 5]
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)
Le résultat est :
Init Test 1 3 5
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.
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)
Le résultat est :
[1] [3] [5]
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()
Le résultat est :
global_var is : 0 outer_var is : 1 inner_var is : 2
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()
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()
Les résultats sont les suivants :
UnboundLocalError: local variable 'num' referenced before assignment
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()
Le résultat du code est :
[1, 2, 3, 4] UnboundLocalError: local variable 'li' referenced before assignment
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()
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
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']
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
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}
在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])
代码结果如下,对于tpl对象,它的每个元素都是不可变的,但是tpl[3]是一个list对象。也就是说,对于这个tpl对象,id(tpl[3])是不可变的,但是tpl[3]确是可变的。
36764576 38639896 (1, 2, 3, [4, 5, 6, 7, 8]) 36764576 38639896
在对Python对象进行赋值的操作中,一定要注意对象的深浅拷贝,一不小心就可能踩坑了。
当使用下面的操作的时候,会产生浅拷贝的效果:
使用切片[:]操作
使用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]
使用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]
在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()
其实,在编码的过程中就应当避免循环依赖的情况,或者代码重构的过程中消除循环依赖。
当然,上面的问题也是可以解决的,常用的解决办法就是把引用关系搞清楚,让某个模块在真正需要的时候再导入(一般放到函数里面)。
对于上面的例子,就可以把module_x.py修改为如下形式,在函数内部导入module_y:
# module_x.py def inc_count(): import module_y module_y.count += 1
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!