Python efficace était efficace comme son nom l'indique

Résumé

J'ai jeté un coup d'œil à Effective Python. Je pense que c'est un très bon livre. Je pense que je suis un débutant en Python. , Je pense qu'il joue un rôle de pont entre les débutants et les intermédiaires plus que suffisant car il contient des choses auxquelles je pense dans une certaine mesure et qui me viennent à l'esprit.Je suis une grande entreprise sur Google. En tant que développeur, il y a des explications qui ne me sont pas intuitivement utiles car j'écris beaucoup de petits codes, mais ce n'est pas la faute de Brett Slatkin, car je ne suis pas moi-même.

Il est facile d'oublier rien qu'en le lisant. Je voudrais résumer certains des points qui m'ont impressionné. Le code utilisé ici est Github [ ^ 1].

Résumé des points d'impression (promotion)

Étant donné que certains extraits sont publiés, il peut être difficile de saisir le contexte, mais veuillez l'acheter et le lire.

Utilisez la formule du générateur!

Vous pouvez utiliser des expressions de générateur pour traiter un grand nombre d'entrées de manière séquentielle:

import random

with open('my_file.txt', 'w') as f:
    for _ in range(10):
        f.write('a' * random.randint(0, 100))
        f.write('\n')

it = (len(x) for x in open('my_file.txt'))
print(next(it))
print(next(it))

Faire tout cela à la fois avec une liste ou un taple consomme beaucoup de mémoire. Je pense que c'est une méthode assez connue, mais Expert Python Programming /books/books/detail/978-4-04-868629-7.shtml) était également impressionnant que la formule du générateur soit fortement recommandée.

N'utilisez pas for ... else, while ... else

C'est un pour ... else qui ne semble pas être utile, mais il semble être la meilleure pratique de ne pas l'utiliser après tout. La raison est que le contexte est différent du sinon général. Il est normal de sauter à else, mais for ... else exécute un bloc ʻelse lorsque l'instruction for se termine avec succès (sans break`), ce qui nuit à la lisibilité. C'est.

Quand je l'ai lu pour la première fois, je me suis dit: "Qu'est-ce que c'est? ...", mais ** "Efficace ..." insiste constamment sur "être générique". ** Programmation L'affirmation selon laquelle l'usage qui mine le sens du mot «autre» dans la langue en général devrait être évité semble naturel maintenant que je l'ai lu. Je pense qu'il est logique de lire.

Qu'est-ce qu'une fermeture?

Je ne sais pas comment dire que la fermeture est un mécanisme de maintien de l'état, donc par exemple:

def closure():
    flag = False

    def getter():
        print(flag)
    return getter


func = closure()
func() # False

func () contient une variable appelée flag. Il est pratique d'utiliser cette technique pour réduire les variables globales.

Une chose à garder à l'esprit est que des choses tristes se produisent lorsque des missions sont impliquées:

def closure():
    flag = False

    def setter():
        flag = True
    setter()
    return flag


print(closure()) # False

J'essaye flag = True avec setter (), mais la valeur de retour de closing () ʻest False, car la portée à l'intérieur de setter () n'a pas flag. Les affectations ont été traitées comme des définitions de variables dans. Si vous voulez profiter des fermetures, vous devez être très prudent avec les affectations. Pour résoudre ce problème, utilisez nonlocal`:

def closure():
    flag = False

    def setter():
        nonlocal flag
        flag = True
    setter()
    return flag


print(closure()) # True

Cela fera référence à la portée supérieure suivante. La racine de ce problème est que les définitions de variables et les affectations ont la même syntaxe en Python. Pour résoudre ce problème, les définitions de variables sont comme Javascript. Il semble qu'il ait été proposé de définir «var flag = False» sur, mais il semble que «nonlocal» ait été adopté pour des raisons de compatibilité.

Les fermetures sont pratiques, mais l'utilisation de nonlocal dans de grandes fonctions rend la compréhension difficile, il semble donc préférable d'utiliser des classes dans de tels cas.

Faites attention à la position de l'argument du mot-clé

En détail, les arguments de mot-clé doivent être spécifiés après les arguments de position:

def remainder(number, divisor):
    return number % divisor


remainder(20, 7)                # OK
remainder(20, divisor=7)        # OK
remainder(number=20, divisor=7) # OK
remainder(divisor=7, number=20) # OK
remainder(number=20, 7)         # NG

N'imbriquez pas de dictionnaires! Utiliser la classe

Les dictionnaires sont très bons en tant que conteneurs, mais les imbriquer de manière à ce qu'ils soient dans le dictionnaire peut être très difficile à maintenir. Avant cela, divisez-les en classes:

import collections
Grade = collections.namedtuple('Grade', ('score', 'weight'))


class Subject(object):
    def __init__(self):
        self._grades = []

    def report_grade(self, score, weight):
        self._grades.append(Grade(score, weight))

    def average_grade(self):
        total, total_weight = 0, 0
        for grade in self._grades:
            total += grade.score * grade.weight
            total_weight += grade.weight
        return total / total_weight


class Student(object):
    def __init__(self):
        self._subjects = {}

    def subject(self, name):
        if name not in self._subjects:
            self._subjects[name] = Subject()
        return self._subjects[name]

    def average_grade(self):
        total, count = 0, 0
        for subject in self._subjects.values():
            total += subject.average_grade()
            count += 1
        return total / count


class Gradebook(object):
    def __init__(self):
        self._students = {}

    def student(self, name):
        if name not in self._students:
            self._students[name] = Student()
        return self._students[name]


book = Gradebook()
albert = book.student('Albert Einstein')
math = albert.subject('Math')
math.report_grade(80, 0.10)
math.report_grade(80, 0.10)
math.report_grade(70, 0.80)
gym = albert.subject('Gym')
gym.report_grade(100, 0.40)
gym.report_grade(85, 0.60)
print(albert.average_grade())

C'est un peu long, mais lisons-le attentivement. ** Cela semble être une réponse à la question "Qu'est-ce qu'une conception de classe facile à maintenir et à étendre?" **

La structure en couches de "1. Carnet de notes → 2. Élèves → 3. Matières → 4. Scores" est réalisée par les variables d'instance (dictionnaires) de chaque objet contenant les objets de la hiérarchie inférieure. Il a une méthode pour définir une instance d'une classe inférieure dans une variable d'instance, et il existe également une méthode pour calculer le score moyen. Si vous regardez de plus près, vous verrez que ce code est très extensible. S'il s'agit d'un seul Il est dangereux d'essayer de le réaliser avec un dictionnaire.

Je ne sais pas si le code ci-dessus est intuitif et facile à comprendre. Je ne l'ai pas compris immédiatement. Mais une fois que je l'ai fait, j'étais convaincu que c'était une idée très simple. D'un autre côté, je pouvais la comprendre. Certains codes ressemblent encore à "Pourquoi avez-vous une implémentation aussi compliquée!?" ** Un code facile à comprendre n'est pas toujours un bon code. Un code facile à expliquer est un bon code (probablement). ** Zen of Python dit aussi que:

Although that way may not be obvious at first unless you're Dutch. La méthode peut être difficile à comprendre à première vue. Seuls les Néerlandais peuvent facilement comprendre.

If the implementation is hard to explain, it's a bad idea. S'il est difficile d'expliquer ce qu'est le code, c'est une mauvaise implémentation.

If the implementation is easy to explain, it may be a good idea. Si vous pouvez facilement expliquer le contenu de votre code, c'est probablement une bonne implémentation.

depuis Le zen de Python

Où utilisez-vous @ classmethod?

Il semble bon de l'utiliser pour la méthode qui crée son propre objet. Considérez la classe parent / classe enfant suivante:

from tempfile import TemporaryDirectory

class GenericInputData(object):
    def read(self):
        raise NotImplementedError

    @classmethod
    def generate_inputs(cls, config):
        raise NotImplementedError


class PathInputData(GenericInputData):
    def __init__(self, path):
        super().__init__()
        self.path = path

    def read(self):
        return open(self.path).read()

    @classmethod
    def generate_inputs(cls, config):
        data_dir = config['data_dir']
        for name in os.listdir(data_dir):
            yield cls(os.path.join(data_dir, name))


class GenericWorker(object):
    def __init__(self, input_data):
        self.input_data = input_data
        self.result = None

    def map(self):
        raise NotImplementedError

    def reduce(self, other):
        raise NotImplementedError

    @classmethod
    def create_workers(cls, input_class, config):
        workers = []
        for input_data in input_class.generate_inputs(config): #Créer une instance de la classe prise comme argument
            workers.append(cls(input_data)) #Créez votre propre instance
        return workers #Renvoie sa propre instance


class LineCountWorker(GenericWorker):
    def map(self):
        data = self.input_data.read()
        self.result = data.count('\n')

    def reduce(self, other):
        self.result += other.result  


with TemporaryDirectory() as tmpdir:
    config = {'data_dir': tmpdir}
    workers = LineCountWorker.create_workers(PathInputData, config)

C'est aussi long, mais faites de votre mieux! Il y a deux points importants.

La première est que la méthode de classe de la classe elle-même est responsable de la création de l'instance. ** Lorsque de nouvelles classes enfants sont ajoutées, c'est la gestion de "où et qui crée l'instance de la classe". Dans l'exemple ci-dessus, LineCountWorker.create_workers () crée sa propre instance sous with, et crée également une instance de PathInputData dedans [^ 2]. En d'autres termes, «où» et «qui le fait» sont clairs.

La seconde est étroitement liée à la première. Cela signifie que " cls est remplacé par la classe appelée. "Cela semble évident, mais très important. Dans le code ci-dessus, * * cls (input_data) défini dans GenericWorker.create_workers () ʻest déformé comme LineCountWorker (input_data) lorsqu'il est appelé via sa classe enfant LineCountWorker. ** Merci à cela, Même si vous créez une nouvelle classe dérivée HogeCountWorker dont la classe parente est GenericWorker, c'est toujours HogeCountWorker.create_workers () qui en crée une instance, et une nouvelle méthode create_workersest créée dans leHogeCountWorker`. Ce n'est pas nécessaire, même s'il s'agit d'une méthode héritée de la classe parente, lorsqu'elle est appelée depuis la classe dérivée, elle se transforme rapidement en une méthode dédiée à cet enfant! C'est le "générique" dont on parle souvent dans "Efficace ...".

Promotion, encore une fois.

Il y a beaucoup d'autres choses utiles telles que des astuces liées aux générateurs d'itérateurs et comment utiliser @ property. Veuillez l'acheter et la lire [^ 3].

Pas si bon

Effective Python est un excellent livre, mais il y a des choses qui m'intéressent. ** Qualité de la traduction. ** Par exemple ...

Exemple 1
(original) You'd want a similar abstract interface for the MapReduce worker that consumes the input data in a standard way.
(Traduction) Supposons que vous souhaitiez une interface abstraite similaire pour les MapReduce Workers qui consomment des données d'entrée en standard.
Exemple 2
(original) Here, I extend the InputData class with a generic class method that's responsible for creating new InputData instances using a common interface.
(Traduction) Étendez la classe InputData et ajoutez une méthode de classe générique qui est responsable de la création de nouvelles instances InputData avec une interface commune.

Hmm ... c'est subtil. J'ai eu beaucoup de doutes quant à savoir si j'ai vraiment compris et écrit le contenu. Surtout, "Chapitre 3: Classe et héritage" a été douloureux. J'ai blâmé mon manque de compréhension pour la traduction. C'était tellement que je voulais ... Non, eh bien, je suis désolé de ne pas être assez intelligent.

Cependant, le contenu en lui-même est toujours merveilleux et il est recommandé à ceux qui maîtrisent l'anglais de lire le texte original.

en conclusion

Je pense que la chose la plus importante dans les livres d'étude, pas seulement la programmation, est un "excellent livre d'introduction." Est évident.

Heureusement, Python regorge de livres d'introduction. Si vous avez un grand nombre de livres, il y en aura parmi eux d'excellents livres. D'un autre côté, qu'en est-il des livres japonais pour débutants et intermédiaires? Il existe de nombreux livres sur Python d'O'Reilly, mais les seuls livres pour débutants qui couvrent un large éventail de personnes sont "Python efficace" et "Python pratique 3", et à l'exception de O'Reilly, "Programmation experte en Python" est probablement le seul [^ 4]. Je suis vraiment content que l'un d'entre eux, "Effective Python", soit sorti avec cette perfection.

Livres de référence

--Brett Slatkin, Python efficace (O'Reilly Japan, 2016)

Recommended Posts

Python efficace était efficace comme son nom l'indique
Obtenez le nom d'hôte en Python
[Python] Récupère le nom de la variable avec str
Définissez le nom du processus du programme Python
Le nom du fichier était mauvais en Python et j'étais accro à l'importation
[Python] J'ai essayé de remplacer le nom de la fonction par le nom de la fonction
Quoi! Le nom du fichier décompressé était SJIS! commander?
[Python] J'ai essayé d'obtenir le nom du type sous forme de chaîne de caractères à partir de la fonction type
Comment obtenir le nom de la variable lui-même en python
Affiche l'heure à partir du moment où le programme a été lancé en python
Programme Python qui recherche le même nom de fichier
Spécifiez MinGW comme compilateur utilisé dans Python
Trouvez le maximum de Python
[Python] Fractionner la date
Identification de nom à l'aide de python
Élément de mémo Python efficace 3
J'étais accro au débogueur Python pdb pendant 2 minutes
[python] Remplacez le nom du fichier image par un numéro de série
Obtenez uniquement la version Python (telle que 2.7.5) sur le shell CentOS 7
En Python, les éléments de la liste sont triés et sortis sous forme d'éléments et de multiples.
'De' == 'De' est faux! En fait, le deuxième "de" était composé de deux lettres. [Python]
Essayez de déchiffrer les caractères déformés dans le nom du fichier joint avec Python