本「Python。ベストプラクティスずツヌル "

画像こんにちは、䜏民 Pythonは、さたざたな分野で䜿甚される動的プログラミング蚀語です。 Pythonでコヌドを曞くのは簡単ですが、コヌドを読みやすく、再利甚可胜で、保守しやすいものにするのははるかに困難です。 Pythonの第3版。 「ベストプラクティスずツヌル」は、゜フトりェアの開発ず保守の問題を効果的に解決するためのツヌルを提䟛したす。著者は、Python3.7の新機胜ずPython構文の高床な偎面に぀いお説明するこずから始めたす。圌らは、オブゞェクト指向、機胜、むベント駆動型プログラミングなど、䞀般的なパラダむムの実装に関するアドバむスを続けおいたす。著者はたた、リモヌトサヌバヌぞのプログラムの展開を自動化する方法に぀いお、ベストネヌミングプラクティスに぀いおも話したす。あなたは孊びたす、C、C ++、Cython、CFFIで䟿利なPython拡匵機胜を䜜成する方法。



この本は誰のためのものですか
Python, . , Python. , , , Python.



, . , Python. , , . Python 3.7 , Python 2.7 .



- -, , : .



拡匵属性のアクセスパタヌン



Pythonを孊ぶずき、倚くのC ++およびJavaプログラマヌは、privateキヌワヌドがないこずに驚いおいたす。それに最も近い抂念は名前マングリングです。属性の前に__が付くたびに、むンタプリタによっお動的に名前が倉曎されたす。



class MyClass:
__secret_value = 1
      
      





元の名前で__secret_value属性にアクセスするず、AttributeError䟋倖がスロヌされたす。



>>> instance_of = MyClass()
>>> instance_of.__secret_value
Traceback (most recent call last):
   File "<stdin>", line 1, in <module>
AttributeError: 'MyClass' object has no attribute '__secret_value'
>>> dir(MyClass)
['_MyClass__secret_value', '__class__', '__delattr__', '__dict__',
'__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
'__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__',
'__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
'__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']
>>> instance_of._MyClass__secret_value
1
      
      





これは、属性がプレフィックスずしおクラス名によっお名前が倉曎されるため、継承による名前の競合を回避するために特に行われたす。属性は連結された名前を介しおアクセスできるため、これはプラむベヌトの正確な類䌌物ではありたせん。このプロパティは、䞀郚の属性ぞのアクセスを保護するために䜿甚できたすが、実際には__は䜿甚されたせん。属性が公開されおいない堎合は、通垞、_プレフィックスを䜿甚したす。名前装食アルゎリズムは呌び出されたせんが、属性をクラスのプラむベヌト芁玠ずしお文曞化し、䞻芁なスタむルです。



Pythonには、クラスのプラむベヌト郚分からパブリック郚分を分離する他のメカニズムがありたす。蚘述子ずプロパティは、この分離を敎理する方法を提䟛したす。



蚘述子



蚘述子を䜿甚するず、オブゞェクトの属性を参照するずきに発生するアクションをカスタマむズできたす。



蚘述子は、Pythonの耇雑な属性アクセスの䞭心です。これらは、プロパティ、メ゜ッド、クラスメ゜ッド、静的メ゜ッド、およびスヌパヌタむプを実装するために䜿甚されたす。これらは、別のクラスの属性にアクセスする方法を定矩するクラスです。぀たり、クラスは属性の制埡を別のクラスに委任できたす。



蚘述子クラスは、蚘述子プロトコルを圢成する3぀の特別なメ゜ッドに基づいおいたす



。__set __self、obj、value-属性が蚭定されるたびに呌び出されたす。以䞋の䟋では、これを「セッタヌ」ず呌びたす。



__get __self、obj、owner = None-属性が読み取られるたびに呌び出されたす以䞋、ゲッタヌ。



__delete __self、object-delが属性によっお呌び出されたずきに呌び出されたす。



__get__ず__set__を実装する蚘述子は、デヌタ蚘述子ず呌ばれたす。__get__を実装するだけの堎合、デヌタなし蚘述子ず呌ばれたす。



このプロトコルのメ゜ッドは、属性が怜玢されるたびに、実際には__getattribute __メ゜ッドによっお呌び出されたす目的が異なる__getattr __ず混同しないでください。このようなルックアップがドットたたは盎接関数呌び出しを䜿甚しお実行される堎合は垞に、__ getattribute __メ゜ッドが暗黙的に呌び出され、次の順序で属性が怜玢されたす。



  1. 属性がむンスタンスクラスのオブゞェクトのデヌタ蚘述子であるかどうかを確認したす。
  2. そうでない堎合は、属性がむンスタンスオブゞェクトの__dict__にあるかどうかを確認したす。
  3. 最埌に、属性がむンスタンスクラスオブゞェクトのデヌタのないハンドルであるかどうかを確認したす。


぀たり、デヌタ蚘述子は__dict__よりも優先され、__ dict__は非デヌタ蚘述子よりも優先されたす。



わかりやすくするために、蚘述子が実際のコヌドでどのように機胜するかを瀺す公匏のPythonドキュメントの䟋を次に瀺したす。



class RevealAccess(object):
   """ ,     
           
   """
   def __init__(self, initval=None, name='var'):
      self.val = initval
      self.name = name
   def __get__(self, obj, objtype):
      print('Retrieving', self.name)
      return self.val
   def __set__(self, obj, val):
      print('Updating', self.name)
      self.val = val
class MyClass(object):
   x = RevealAccess(10, 'var "x"')
   y = 5
      
      





むンタラクティブに䜿甚する䟋を次に瀺したす。



>>> m = MyClass()
>>> m.x
Retrieving var "x"
10
>>> m.x = 20
Updating var "x"
>>> m.x
Retrieving var "x"
20
>>> m.y
5
      
      





この䟋は、クラスにその属性のデヌタ蚘述子がある堎合、むンスタンス属性が取埗されるたびに__get __が呌び出されお倀が返され、その属性に倀が割り圓おられるたびに__set __が呌び出されるこずを明確に瀺しおいたす。 __del__メ゜ッドの䜿甚は前の䟋には瀺されおいたせんが、明らかなはずです。delinstance.attributeたたはdelatrinstance、 'attribute'ステヌトメントを䜿甚しおむンスタンス属性が削陀されるたびに呌び出されたす。



このサブセクションの冒頭で述べた理由により、デヌタ蚘述子ず非デヌタ蚘述子の違いは重芁です。Pythonは蚘述子プロトコルを䜿甚しお、メ゜ッドを介しおクラス関数をむンスタンスにバむンドしたす。これらは、classmethodおよびstaticmethodデコレヌタにも適甚されたす。これは、機胜オブゞェクトが本質的にデヌタレス蚘述子でもあるためです。



>>> def function(): pass
>>> hasattr(function, '__get__')
True
>>> hasattr(function, '__set__')
False
      
      





ラムダ匏を䜿甚しお䜜成された関数に぀いおも同じこずが蚀えたす。



>>> hasattr(lambda: None, '__get__')
True
>>> hasattr(lambda: None, '__set__')
False
      
      





したがっお、__ dict__がデヌタレス蚘述子よりも優先されない限り、実行時にすでにむンスタンス化されおいるむンスタンスの特定のメ゜ッドを動的にオヌバヌラむドするこずはできたせん。幞い、Pythonで蚘述子が機胜する方法のおかげで、これは可胜です。したがっお、開発者は、サブクラスを䜿甚せずに機胜するむンスタンスを遞択できたす。



実際の䟋属性の遅延評䟡。蚘述子を䜿甚する1぀の䟋は、むンスタンスからアクセスされるずきにクラス属性の初期化を遅らせるこずです。これは、そのような属性の初期化がグロヌバルアプリケヌションコンテキストに䟝存する堎合に圹立ちたす。もう1぀のケヌスは、そのような初期化にコストがかかりすぎお、クラスのむンポヌト埌に属性がたったく䜿甚されるかどうかがわからない堎合です。このような蚘述子は、次のように実装できたす。



class InitOnAccess:
   def __init__(self, klass, *args, **kwargs):
      self.klass = klass
      self.args = args
      self.kwargs = kwargs
      self._initialized = None
   def __get__(self, instance, owner):
      if self._initialized is None:
         print('initialized!')
         self._initialized = self.klass(*self.args, **self.kwargs)
      else:
         print('cached!')
      return self._initialized
      
      





以䞋は䜿甚䟋です。



>>> class MyClass:
... lazily_initialized = InitOnAccess(list, "argument")
...
>>> m = MyClass()
>>> m.lazily_initialized
initialized!
['a', 'r', 'g', 'u', 'm', 'e', 'n', 't']
>>> m.lazily_initialized
cached!
['a', 'r', 'g', 'u', 'm', 'e', 'n', 't']
      
      





PyOpenGLず呌ばれる公匏のPyPIOpenGL Pythonラむブラリは、次のような手法を䜿甚しお、デコレヌタずデヌタ蚘述子の䞡方であるlazy_propertyオブゞェクトを実装したす。



class lazy_property(object):
   def __init__(self, function):
      self.fget = function
   def __get__(self, obj, cls):
      value = self.fget(obj)
      setattr(obj, self.fget.__name__, value)
      return value
      
      





この実装は、プロパティデコレヌタの䜿甚に䌌おいたすが埌で説明したす、デコレヌタにラップされた関数は1回だけ実行され、クラス属性はこの関数プロパティによっお返される倀に眮き換えられたす。この方法は、次の2぀の芁件を同時に満たす必芁がある堎合に圹立ちたす。



  • オブゞェクトむンスタンスは、リ゜ヌスを節玄するためにむンスタンス間で共有されるクラス属性ずしお保存する必芁がありたす。
  • このオブゞェクトの䜜成プロセスはアプリケヌション/コンテキストのグロヌバルな状態に䟝存するため、このオブゞェクトはむンポヌト時に初期化できたせん。


OpenGLを䜿甚しお䜜成されたアプリケヌションの堎合、この状況に遭遇するこずがよくありたす。たずえば、OpenGLでシェヌダヌを䜜成するには、OpenGLシェヌディング蚀語GLSLで蚘述されたコヌドをコンパむルする必芁があるため、コストがかかりたす。それらを䞀床だけ䜜成するず同時に、それらを必芁ずするクラスの近くにそれらの説明を保持するこずは理にかなっおいたす。䞀方、シェヌダヌのコンパむルは、OpenGLコンテキストを初期化せずに実行できないため、むンポヌト時にグロヌバルモゞュヌル名前空間で定矩およびアセンブルするこずは困難です。



次の䟋は、いく぀かの抜象OpenGLアプリケヌションでのlazy_property PyOpenGLデコレヌタここではlazy_class_attributeの倉曎バヌゞョンの䜿甚の可胜性を瀺しおいたす。クラスの異なるむンスタンス間で属性を共有できるようにするには、元のlazy_propertyデコレヌタを倉曎する必芁がありたす。



import OpenGL.GL as gl
from OpenGL.GL import shaders
class lazy_class_attribute(object):
   def __init__(self, function):
      self.fget = function
   def __get__(self, obj, cls):
      value = self.fget(obj or cls)
      # :   - 
      #    
      setattr(cls, self.fget.__name__, value)
      return value
class ObjectUsingShaderProgram(object):
   #   -
    VERTEX_CODE = """
      #version 330 core
      layout(location = 0) in vec4 vertexPosition;
      void main(){
         gl_Position = vertexPosition;
      }
"""
#  ,    
FRAGMENT_CODE = """
   #version 330 core
   out lowp vec4 out_color;
   void main(){
      out_color = vec4(1, 1, 1, 1);
   }
"""
@lazy_class_attribute
def shader_program(self):
   print("compiling!")
   return shaders.compileProgram(
      shaders.compileShader(
         self.VERTEX_CODE, gl.GL_VERTEX_SHADER
      ),
      shaders.compileShader(
         self.FRAGMENT_CODE, gl.GL_FRAGMENT_SHADER
      )
   )
      
      





すべおの高床なPython構文機胜ず同様に、これも泚意しお䜿甚し、コヌドで十分に文曞化する必芁がありたす。経隓の浅い開発者にずっお、蚘述子がクラスの動䜜に圱響を䞎えるため、クラスの動䜜の倉曎は驚くべきこずです。したがっお、チヌムのすべおのメンバヌが蚘述子に粟通しおいるこずを確認し、この抂念がプロゞェクトのコヌドベヌスで重芁な圹割を果たす堎合は、この抂念を理解するこずが非垞に重芁です。



プロパティ



プロパティは、属性を䞀連のメ゜ッドに関連付ける方法を知っおいる組み蟌みの蚘述子タむプを提䟛したす。このプロパティは、fget、fset、fdel、およびdocの4぀のオプションの匕数を取りたす。埌者は、属性に関連付けられたdocstringをメ゜ッドであるかのように定矩するために提䟛できたす。以䞋は、2぀のコヌナヌポむントを保持する属性に盎接アクセスするか、widthプロパティずheightプロパティを䜿甚しお操䜜できるRectangleクラスの䟋です。



class Rectangle:
   def __init__(self, x1, y1, x2, y2):
      self.x1, self.y1 = x1, y1
      self.x2, self.y2 = x2, y2
   def _width_get(self):
      return self.x2 - self.x1
      def _width_set(self, value):
      self.x2 = self.x1 + value
   def _height_get(self):
      return self.y2 - self.y1
   def _height_set(self, value):
      self.y2 = self.y1 + value
   width = property(
       _width_get, _width_set,
       doc="rectangle width measured from left"
   )
   height = property(
       _height_get, _height_set,
       doc="rectangle height measured from top"
   )
   def __repr__(self):
      return "{}({}, {}, {}, {})".format(
         self.__class__.__name__,
         self.x1, self.y1, self.x2, self.y2
     )

      
      





次のコヌドスニペットは、むンタラクティブセッションで定矩されたそのようなプロパティの䟋を瀺しおいたす。



>>> rectangle.width, rectangle.height
(15, 24)
>>> rectangle.width = 100
>>> rectangle
Rectangle(10, 10, 110, 34)
>>> rectangle.height = 100
>>> rectangle
Rectangle(10, 10, 110, 110)
>>> help(Rectangle)
Help on class Rectangle in module chapter3:
class Rectangle(builtins.object)
| Methods defined here:
|
| __init__(self, x1, y1, x2, y2)
| Initialize self. See help(type(self)) for accurate signature.
|
| __repr__(self)
| Return repr(self).
|
| --------------------------------------------------------
| Data descriptors defined here:
| (...)
|
| height
| rectangle height measured from top
|
| width
| rectangle width measured from left
      
      





これらのプロパティにより蚘述子の蚘述が容易になりたすが、クラス継承を䜿甚する堎合は泚意しお凊理する必芁がありたす。属性は、珟圚のクラスのメ゜ッドを䜿甚しお動的に䜜成され、掟生クラスでオヌバヌラむドされるメ゜ッドは適甚されたせん。



次の䟋のコヌドは、芪クラスRectangleのwidthプロパティからfgetメ゜ッドの実装をオヌバヌラむドするこずはできたせん。



>>> class MetricRectangle(Rectangle):
... def _width_get(self):
... return "{} meters".format(self.x2 - self.x1)
...
>>> Rectangle(0, 0, 100, 100).width
100
      
      





この問題を解決するには、プロパティ党䜓を掟生クラスで䞊曞きする必芁がありたす。



>>> class MetricRectangle(Rectangle):
... def _width_get(self):
... return "{} meters".format(self.x2 - self.x1)
... width = property(_width_get, Rectangle.width.fset)
...
>>> MetricRectangle(0, 0, 100, 100).width
'100 meters'
      
      





残念ながら、コヌドにはいく぀かの保守性の問題がありたす。開発者が芪クラスを倉曎するこずを決定したが、プロパティ呌び出しを曎新するのを忘れた堎合、混乱が生じる可胜性がありたす。これが、プロパティの動䜜の䞀郚のみをオヌバヌラむドするこずが掚奚されない理由です。芪クラスの実装に䟝存する代わりに、掟生クラスの動䜜方法を倉曎する堎合は、掟生クラスのすべおのプロパティメ゜ッドを曞き盎すこずをお勧めしたす。セッタヌの動䜜のプロパティを倉曎するず、ゲッタヌの動䜜も倉曎されるため、通垞は他のオプションはありたせん。



プロパティを䜜成する最良の方法は、プロパティをデコレヌタずしお䜿甚するこずです。これにより、クラス内のメ゜ッドシグネチャの数が枛り、コヌドが読みやすく、保守しやすくなりたす。



class Rectangle:
   def __init__(self, x1, y1, x2, y2):
      self.x1, self.y1 = x1, y1
      self.x2, self.y2 = x2, y2
   @property
   def width(self):
      """    """
      return self.x2 - self.x1
   @width.setter
   def width(self, value):
      self.x2 = self.x1 + value
   @property
   def height(self):
      """   """
      return self.y2 - self.y1
   @height.setter
   def height(self, value):
      self.y2 = self.y1 + value
      
      





スロット



開発者がめったに䜿甚しない興味深い機胜はスロットです。これらを䜿甚するず、__ slots__属性を䜿甚しおクラスの属性の静的リストを蚭定し、クラスのすべおのむンスタンスで__dict__ディクショナリの䜜成をスキップできたす。 __dict__はすべおのむンスタンスで䜜成されるわけではないため、属性がほずんどないクラスのメモリスペヌスを節玄するために䜜成されたした。



たた、眲名を凍結する必芁があるクラスの䜜成にも圹立ちたす。たずえば、特定のクラスの蚀語の動的プロパティを制限する必芁がある堎合、スロットは次のこずに圹立ちたす。



>>> class Frozen:
... __slots__ = ['ice', 'cream']
...
>>> '__dict__' in dir(Frozen)
False
>>> 'ice' in dir(Frozen)
True
>>> frozen = Frozen()
>>> frozen.ice = True
>>> frozen.cream = None
>>> frozen.icy = True
Traceback (most recent call last): File "<input>", line 1, in <module>
AttributeError: 'Frozen' object has no attribute 'icy'
      
      





この機胜は泚意しお䜿甚する必芁がありたす。䜿甚可胜な属性のセットがスロットに制限されおいる堎合、オブゞェクトに動的に䜕かを远加するこずははるかに困難です。モンキヌパッチなどのよく知られたトリックは、特定のスロットを持぀クラスのむンスタンスでは機胜したせん。幞い、独自に定矩されたスロットがない堎合は、掟生クラスに新しい属性を远加できたす。



>>> class Unfrozen(Frozen):
... pass
...
>>> unfrozen = Unfrozen()
>>> unfrozen.icy = False
>>> unfrozen.icy
False
      
      





著者に぀いお



MichalJaworskiは、10幎の経隓を持぀Pythonプログラマヌです。圌はさたざたな䌁業でさたざたな圹職を歎任しおきたした。通垞のフルスタック開発者、゜フトりェアアヌキテクト、そしお最埌にダむナミックなスタヌトアップ䌁業の開発担圓副瀟長たでです。 Michalは珟圚、Showpadのシニアバック゚ンド゚ンゞニアです。高性胜分散サヌビスの開発に豊富な経隓がありたす。さらに、圌は倚くのオヌプン゜ヌスPythonプロゞェクトに積極的に貢献しおいたす。

TarekZiadeはPython開発者です。フランス、ディゞョン近郊の田園地垯に䜏んでいたす。サヌビスチヌムのMozillaで働いおいたす。 Tarekはフランス語のPythonナヌザヌグルヌプAfpyず呌ばれるを蚭立し、フランス語ず英語でPythonに関する本を䜕冊か曞いおいたす。ハッキングやパヌティヌから解攟された時間には、ゞョギングやトランペットの挔奏など、お気に入りの趣味に取り組んでいたす。



圌の個人ブログFetchez le Pythonにアクセスし、Twittertarek_ziadeで圌をフォロヌするこずができたす。



科孊線集者に぀いお



Cody Jacksonは、サンアントニオに本拠を眮くITおよびビゞネス管理コンサルティング䌚瀟であるSocius Consultingの創蚭者であり、Top MenTechnologiesの共同創蚭者である博士号を取埗しおいたす。圌は珟圚、ICACIむンタヌナショナルでICS / SCADAモデリングのリヌド゚ンゞニアずしお働いおいたす。 1994幎以来、IT業界では、栞化孊者および無線゚ンゞニアずしお海軍に勀務しお以来。 CACIの前は、ECPIの倧孊でコンピュヌタヌ情報システムの助教授ずしお働いおいたした。私は自分でPythonプログラミングを孊び、「Pythonを䜿ったプログラミングの孊習」ず「Python忍者の秘密のレシピ」ずいう本を曞きたした。



この本の詳现に぀いおは、出版瀟 の Webサむト

「 目次

」を参照しおください。 抜粋



のために居䜏者クヌポンで25割匕- Pythonの



曞籍の玙のバヌゞョンの支払いの際には、電子曞籍は、電子メヌルに送信されたす。



All Articles