À quel point les noms d'observation sont-ils définis dans les étendues externes?

Je viens de passer à Pycharm et je suis très heureux de tous les avertissements et astuces qu'il me fournit pour améliorer mon code. Sauf pour celui - ci que je ne comprends pas:

This inspection detects shadowing names defined in outer scopes.

Je sais que c'est une mauvaise pratique d'accéder à la variable à partir de la portée externe, mais quel est le problème avec l'observation de la portée externe?

Voici un exemple, où Pycharm me donne le message d'avertissement:

data = [4, 5, 6]

def print_data(data): # <-- Warning: "Shadows 'data' from outer scope
    print data

print_data(data)
120
demandé sur Framester 2013-11-21 19:35:11

7 réponses

Pas grand chose dans votre extrait ci-dessus, mais imaginez une fonction avec quelques arguments de plus et quelques lignes de code supplémentaires. Ensuite, vous décidez de renommer votre argument data en yadda mais manquez l'un des endroits où il est utilisé dans le corps de la fonction... Maintenant data fait référence au global, et vous commencez à avoir un comportement étrange - où vous auriez un NameError beaucoup plus évident si vous n'aviez pas de nom global data.

Rappelez-vous aussi qu'en Python tout est un objet (y compris les modules, les classes et fonctions) donc il n'y a pas d'espaces de noms distincts pour les fonctions, les modules ou les classes. Un autre scénario est que vous importez la fonction foo en haut de votre module, et l'utilisez quelque part dans votre corps de fonction. Ensuite, vous ajoutez un nouvel argument à votre fonction et l'a nommé - malchance - foo.

Enfin, les fonctions et les types intégrés vivent également dans le même espace de noms et peuvent être ombrés de la même manière.

Rien de tout cela n'est un problème si vous avez des fonctions courtes, un bon nom et un couverture unittest, mais bien, parfois vous devez maintenir un code moins que parfait et être averti de ces problèmes possibles pourrait aider.

142
répondu bruno desthuilliers 2015-05-10 19:26:56

La réponse actuellement la plus votée et acceptée et la plupart des réponses ici manquent le point.

Peu importe la durée de votre fonction, ou comment vous nommez votre variable de manière descriptive (pour, espérons-le, minimiser le risque de collision de nom potentielle).

Le fait que la variable locale de votre fonction ou son paramètre partage un nom dans la portée globale est complètement hors de propos. Et en fait, peu importe avec quel soin vous choisissez votre nom de variable locale, votre fonction ne peut jamais prévoir "si mon nom cool yadda sera également utilisé comme variable globale à l'avenir?". La solution? Tout simplement ne vous inquiétez pas à ce sujet! L'état d'esprit correct est de concevoir votre fonction pour consommer l'entrée de et seulement de ses paramètres dans la signature, de cette façon vous n'avez pas besoin de vous soucier de ce qui est (ou sera) dans la portée globale, puis l'observation ne devient pas un problème du tout.

En d'autres termes, le problème d'observation n'a d'importance que lorsque votre fonction doit utiliser la même variable locale de nom et variable globale. Mais vous devriez éviter une telle conception en premier lieu. Le code de L'OP n'a pas vraiment un tel problème de conception. C'est juste que PyCharm n'est pas assez intelligent et il donne un avertissement au cas où. Donc, juste pour rendre PyCharm heureux, et aussi rendre notre code propre, voir cette solution citant la réponse de silyevsk pour supprimer complètement la variable globale.

def print_data(data):
    print data

def main():
    data = [4, 5, 6]
    print_data(data)

main()

C'est la bonne façon de "résoudre" ce problème, en corrigeant / supprimant votre truc global, sans ajuster votre fonction locale actuelle.

60
répondu RayLuo 2017-08-29 14:38:49

Une bonne solution de contournement dans certains cas peut être de déplacer le code vars + vers une autre Fonction:

def print_data(data):
    print data

def main():
    data = [4, 5, 6]
    print_data(data)

main()
12
répondu silyevsk 2016-08-29 08:22:52
data = [4, 5, 6] #your global variable

def print_data(data): # <-- Pass in a parameter called "data"
    print data  # <-- Note: You can access global variable inside your function, BUT for now, which is which? the parameter or the global variable? Confused, huh?

print_data(data)
5
répondu JoeC 2013-11-21 15:42:07

Cela dépend de la durée de la fonction. Plus la fonction est longue, plus il y a de chances que quelqu'un la modifiant à l'avenir écrive data en pensant que cela signifie le global. En fait, cela signifie le local mais parce que la fonction est si longue qu'il n'est pas évident pour eux qu'il existe un local avec ce nom.

Pour votre exemple de fonction, je pense que l'observation du global n'est pas Mauvaise du tout.

5
répondu Steve Jessop 2013-11-21 15:55:37

Faites ceci:

data = [4, 5, 6]

def print_data():
    global data
    print(data)

print_data()
4
répondu dR1pT 2017-08-23 11:13:54

, Il ressemble à 100% pytest la structure du code

Voir:

Https://docs.pytest.org/en/latest/fixture.html#conftest-py-sharing-fixture-functions

J'ai eu le même problème avec, c'est pourquoi j'ai trouvé ce post ;)

# ./tests/test_twitter1.py
import os
import pytest

from mylib import db
# ...

@pytest.fixture
def twitter():
    twitter_ = db.Twitter()
    twitter_._debug = True
    return twitter_

@pytest.mark.parametrize("query,expected", [
    ("BANCO PROVINCIAL", 8),
    ("name", 6),
    ("castlabs", 42),
])
def test_search(twitter: db.Twitter, query: str, expected: int):

    for query in queries:
        res = twitter.search(query)
        print(res)
        assert res

Et il avertira avec This inspection detects shadowing names defined in outer scopes.

Pour corriger cela, déplacez simplement votre appareil twitter dans ./tests/conftest.py

# ./tests/conftest.py
import pytest

from syntropy import db


@pytest.fixture
def twitter():
    twitter_ = db.Twitter()
    twitter_._debug = True
    return twitter_

Et supprimer twitter luminaire comme dans ./tests/test_twitter2.py

# ./tests/test_twitter2.py
import os
import pytest

from mylib import db
# ...

@pytest.mark.parametrize("query,expected", [
    ("BANCO PROVINCIAL", 8),
    ("name", 6),
    ("castlabs", 42),
])
def test_search(twitter: db.Twitter, query: str, expected: int):

    for query in queries:
        res = twitter.search(query)
        print(res)
        assert res

Ce sera rendre heureux QA, Pycharm et tout le monde

1
répondu Andrei.Danciuc 2018-02-04 11:44:06