La troisième histoire d'Halloween.
Par exemple, supposons que vous ayez ce code. C'est un code Fizzbuzz courant.
Fizzbuzz sans aucun changement
def fizzbuzz(n):
if n % 15 == 0:
return 'fizzbuzz'
if n % 3 == 0:
return 'fizz'
if n % 5 == 0:
return 'buzz'
return str(n)
for i in range(20):
print(fizzbuzz(i+1))
1
2
fizz
4
buzz
fizz
7
8
fizz
buzz
11
fizz
13
14
fizzbuzz
16
17
fizz
19
buzz
Cependant, ʻi + 1 de
fizzbuzz (i + 1) `a l'air moche.
Puisque vous êtes coincé dans l'instruction for, ne voulez-vous pas que je lise les arguments et devine? [^ 1] Je veux que cela fonctionne de la même manière même si je l'appelle comme suit.
for _ in range(20):
print(fizzbuzz())
Contester l'argument réel pour se cacher
[^ 1]: Je ne pense pas.
J'ai fait quelque chose comme ça.
Produit final
#↓ Ne vous inquiétez pas du droit à partir d'ici
def fizzbuzz(n=(lambda mi: (__import__('sys').setprofile(mi.hook), mi)[-1])(type('MyInt', (object, ), {'__init__': (lambda self, n: setattr(self, '_n', n)), 'hook': (lambda self, frame, event, _: setattr(self, '_n', self._n + ((frame.f_code.co_name, event) == ('fizzbuzz', 'return')))), '__mod__': (lambda self, other: self._n % other), '__str__': (lambda self: str(self._n)),})(1))):
if n % 15 == 0:
return 'fizzbuzz'
if n % 3 == 0:
return 'fizz'
if n % 5 == 0:
return 'buzz'
return str(n)
for _ in range(20):
print(fizzbuzz())
** Résultat de l'exécution ** ⇒ Wandbox
Bien entendu, il peut être exécuté normalement et le résultat de l'exécution est le même qu'avant la modification. Cet article explique comment ce code a été complété.
Si vous omettez simplement les arguments réels, il existe de nombreuses façons de procéder. Dans cette section, je voudrais clarifier les objectifs en écrivant chaque solution simple et ses difficultés ~~ (ou habitudes difficiles) ~~.
default_n = 1
def fizzbuzz(n=None):
global default_n
if n is None:
n = default_n
default_n += 1
if n % 15 == 0:
...
Une mauvaise déclaration globale est apparue et l'implémentation de la fonction doit être modifiée. C'est hors de question.
def make_fizzbuzz(n_start=1):
default_n = n_start
def _fizzbuzz(n=None):
nonlocal default_n
if n is None:
n = default_n
default_n += 1
if n % 15 == 0:
...
return _fizzbuzz
fizzbuzz = make_fizzbuzz()
Il y a quelques avantages par rapport à la méthode utilisant la variable de module précédente.
--La valeur initiale de n (n_start) peut être spécifiée lorsque la fonction fizzbuzz est générée.
Cependant, l'implémentation de la fonction fizzbuzz doit être considérablement modifiée.
class CountedUpInt:
def __init__(self, start_n=1):
self._n = start_n
def __int__(self):
n = self._n
self._n += 1
return n
def fizzbuzz(n=CountedUpInt()):
n = int(n)
if n % 15 == 0:
...
Une solution simple. Facile à comprendre [^ 2] et facile à réparer.
[^ 2]: Cependant, vous devez comprendre comment gérer les arguments par défaut de Python. La ** valeur ** par défaut n'est évaluée qu'une seule fois lorsque la fonction est définie. Eh bien, même avec l'expression ** par défaut **, vous pouvez créer un code similaire en utilisant des variables de classe.
Cependant, même avec cette méthode, l'intérieur de l'implémentation de la fonction fizzbuzz doit être modifié. ** Le but est de réaliser que Dieu se cache simplement en concevant les arguments par défaut sans toucher à la partie code de la fonction fizzbuzz. ** **
Si vous faites bon usage des fonctions d'ordre supérieur, vous n'aurez pas à toucher à l'implémentation de la fonction fizzbuzz. La lisibilité est également améliorée lorsqu'un décorateur est utilisé ensemble.
def countup_wrapper(n_start):
default_n = n_start
def _inner1(func):
def _inner2(n=None):
nonlocal default_n
if n is None:
n = default_n
default_n += 1
return func(n)
return _inner2
return _inner1
@countup_wrapper(1)
def fizzbuzz(n):
...
Je l'ai inventé après avoir fini d'écrire la plupart des articles. ~~ ** Est-ce que ça va? ** ~~ L'article que j'ai écrit sera gaspillé, alors prenez l'allergie non locale comme un bouclier et ignorez-la.
C'est une liaison qui ne modifie que l'argument par défaut.
Tant que vous ne touchez pas la partie de code, la valeur par défaut doit pouvoir être traitée de la même manière qu'une valeur numérique. Par exemple, afin de réaliser le calcul du surplus, la classe doit être organisée comme suit. [^ 3]
[^ 3]: Si vous voulez mettre l'objet MyInt dans l'opérande droit du calcul du reste, vous devez également implémenter la méthode __rmod__
.
class MyInt:
def __init__(self, n):
self._n = n
def __mod__(self, other):
return self._n % other
def __str__(self):
return str(self._n)
print(16 % 6) # => 4
print(MyInt(16) % 6) # => 4
«mod» est une méthode spéciale responsable du calcul du reste, et «a% b» équivaut à «a .__ mod__ (b)». ** Référence **: [Référence du langage Python »3. Modèle de données» 3.3.8. Émuler les types numériques](https://docs.python.org/ja/3/reference/datamodel.html#emulating- numeric-types)
À ce stade, vous pouvez écrire du code comme celui-ci: Cependant, comme le processus de comptage n'a pas été mis en œuvre, le même nombre sera naturellement déterminé de manière répétée.
def fizzbuzz(n=MyInt(1)):
if n % 15 == 0:
...
Si vous n'êtes pas impliqué dans l'implémentation de la fonction fizzbuzz, vous n'avez pas d'autre choix que de raccrocher l'appel / échappement de la fonction. Ici, réalisons le hook en utilisant la fonction sys.setprofile ~~ abuse ~~ ** diversion **. Je pense.
class MyInt:
...
def succ(self):
self._n += 1
...
import sys
myInt = MyInt(1)
def _hook(frame, event, arg):
# frame.f_code.co_nom Nom de la fonction de l'événement
# event 'call'Ou'return'
if (frame.f_code.co_name, event) == ('fizzbuzz', 'return'):
myInt.succ()
sys.setprofile(_hook)
#
def fizzbuzz(n=myInt):
if n % 15 == 0:
...
** Référence **: bibliothèque standard Python »inspect --types and members
A ce stade, l'objectif mentionné ci-dessus "sans toucher à la partie de code de la fonction fizzbuzz" a déjà été atteint.
Livrable α
class MyInt:
def __init__(self, n):
self._n = n
def succ(self):
self._n += 1
def __mod__(self, other):
return self._n % other
def __str__(self):
return str(self._n)
import sys
myInt = MyInt(1)
def _hook(frame, event, arg):
if (frame.f_code.co_name, event) == ('fizzbuzz', 'return'):
myInt.succ()
sys.setprofile(_hook)
#
def fizzbuzz(n=myInt):
if n % 15 == 0:
return 'fizzbuzz'
if n % 3 == 0:
return 'fizz'
if n % 5 == 0:
return 'buzz'
return str(n)
for _ in range(20):
print(fizzbuzz())
** Résultat de l'exécution ** ⇒ Wandbox
Cependant, il y a trop de descriptions autres que la fonction fizzbuzz dans le produit α précédent. Pour souligner que fizzbuzz est le but principal, ** je vais l'écrire avec le moins de lignes possible ** [^ 4].
[^ 4]: Ne dites pas que le style artistique est restreint. La silhouette est trop étoilée et blessée.
Afin de rendre la description ultérieure aussi simple que possible, je vais résumer les objets dans une certaine mesure. Par exemple, la fonction _hook peut être incorporée en tant que méthode de la classe MyInt.
class MyInt:
...
def hook(self, frame, event, arg):
if (frame.f_code.co_name, event) == ('fizzbuzz', 'return'):
self._n += 1
myInt = MyInt(1)
sys.setprofile(myInt.hook)
La méthode succ a été supprimée pour faciliter les modifications ultérieures.
Implémentez la méthode avec une seule instruction d'expression ou une seule instruction de retour.
(valeur 1, valeur 2) [condition]
, mais ici je voudrais profiter du fait que Vrai / Faux sont respectivement 1/0. ..[^ 5]: Python 3.8 a introduit l'opérateur d'affectation (communément appelé opérateur Seiuchi), mais il semble y avoir une limite au contexte dans lequel il peut être utilisé. [^ 6]: Je veux écrire un traitement sans état sans l'utiliser autant que possible. Est-ce que le sentiment de perdre si j'utilise setattr facilement n'est que pour moi?
class MyInt:
def __init__(self, n):
setattr(self, '_n', n)
def hook(self, frame, event, arg):
setattr(self, '_n',
#Soi quand la condition est vraie._n +Équivalent à 1, soi si faux._n +Équivaut à 0.
self._n + ((frame.f_code.co_name, event) == ('fizzbuzz', 'return'))
)
...
Redéfinissez chaque méthode à l'aide d'une expression lambda.
class MyInt:
__init__ = (lambda self, n:
setattr(self, '_n', n)
)
hook = (lambda self, frame, event, _:
setattr(
self, '_n',
self._n + ((frame.f_code.co_name, event) == ('fizzbuzz', 'return'))
)
)
__mod__ = (lambda self, other:
self._n % other
)
__str__ = (lambda self:
str(self._n)
)
Puisqu'une méthode est une variable de classe après tout, son remplacement est étonnamment facile.
Vous pouvez utiliser la fonction type pour créer un objet de classe dans une expression. Les attributs de classe sont transmis ensemble dans un dictionnaire.
MyInt = type('MyInt', (object, ), {
'__init__': (lambda self, n:
setattr(self, '_n', n)
),
'hook': (lambda self, frame, event, _:
setattr(
self, '_n',
self._n + ((frame.f_code.co_name, event) == ('fizzbuzz', 'return'))
)
),
'__mod__': (lambda self, other:
self._n % other
),
'__str__': (lambda self:
str(self._n)
),
})
À ce stade, la définition de classe est une ligne.
MyInt = type('MyInt', (object, ), {'__init__': (lambda self, n: setattr(self, '_n', n)), 'hook': (lambda self, frame, event, _: setattr(self, '_n', self._n + ((frame.f_code.co_name, event) == ('fizzbuzz', 'return')))), '__mod__': (lambda self, other: self._n % other), '__str__': (lambda self: str(self._n)),})
Vous pouvez utiliser la fonction __import__
au lieu de l'instruction d'importation et écrire:
__import__('sys').setprofile(MyInt(1).hook)
En fait, ce code est incomplet. L'instance MyInt que j'ai créée a été supprimée et ne peut plus être récupérée [^ 7].
[^ 7]: Strictement parlant, c'est un mensonge. Cependant, collecter avec myInt = sys.getprofile () .__ self__
est ennuyeux.
Il est courant de faire bon usage de la formule lambda comme suit.
myInt = (lambda mi:
#Créez un taple et renvoyez son dernier élément.
(__import__('sys').setprofile(mi.hook), mi)[-1]
#Créez un objet une seule fois.
)(MyInt(1))
Intégrez simplement l'objet type précédent directement dans la partie MyInt.
myInt = (lambda mi:
(__import__('sys').setprofile(mi.hook), mi)[-1]
)(type('MyInt', (object, ), {'__init__': (lambda self, n: setattr(self, '_n', n)), 'hook': (lambda self, frame, event, _: setattr(self, '_n', self._n + ((frame.f_code.co_name, event) == ('fizzbuzz', 'return')))), '__mod__': (lambda self, other: self._n % other), '__str__': (lambda self: str(self._n)),})(1))
Utilisez l'objet ci-dessus pour la valeur par défaut myInt de l'argument n pour obtenir le produit final mentionné ci-dessus.
Livrable β(Produit final)
def fizzbuzz(n=(lambda mi: (__import__('sys').setprofile(mi.hook), mi)[-1])(type('MyInt', (object, ), {'__init__': (lambda self, n: setattr(self, '_n', n)), 'hook': (lambda self, frame, event, _: setattr(self, '_n', self._n + ((frame.f_code.co_name, event) == ('fizzbuzz', 'return')))), '__mod__': (lambda self, other: self._n % other), '__str__': (lambda self: str(self._n)),})(1))):
if n % 15 == 0:
return 'fizzbuzz'
if n % 3 == 0:
return 'fizz'
if n % 5 == 0:
return 'buzz'
return str(n)
for _ in range(20):
print(fizzbuzz())
** Résultat de l'exécution ** ⇒ Wandbox
Comme mentionné ci-dessus, j'ai pu écrire le code sans les arguments réels. Si vous pouvez omettre les arguments évidents lors de l'écriture de code ... N'aimeriez-vous en aucun cas le faire?
** Je ne pense pas. ** **
Répertoriez les fonctions qui pourront être étendues à l'avenir.
Image de l'extension 1
print(fizzbuzz()) # => 1
print(fizzbuzz()) # => 2
print(fizzbuzz()) # => fizz
fizzbuzz.reset(10)
print(fizzbuzz()) # => buzz
print(fizzbuzz()) # => 11
Image de l'extension 2
print(fizzbuzz()) # => 1
print(fizzbuzz()) # => 2
print(fizzbuzz()) # => fizz
print(fizzbuzz(10)) # => buzz
print(fizzbuzz()) # => 11
La mise en œuvre temporaire de l'extension 3 est terminée à ce stade. Je suis également intéressé par 1 et 2, donc j'écrirai bientôt à leur sujet. Je ne sais pas si ce sera un article.
Recommended Posts