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].
Étant donné que certains extraits sont publiés, il peut être difficile de saisir le contexte, mais veuillez l'acheter et le lire.
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.
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.
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.
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
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
@ 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 le
HogeCountWorker`. 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 ...".
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].
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.
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.
--Brett Slatkin, Python efficace (O'Reilly Japan, 2016)
Recommended Posts