Hướng dẫn c3 linearization algorithm python - python thuật toán tuyến tính hóa c3

  • Techblog
  • Software Engineering

Hướng dẫn c3 linearization algorithm python - python thuật toán tuyến tính hóa c3

Trong phần trước, mình đã đề cập các tính chất cơ bản của class trong Python. Trong số này, mình sẽ đề cập về cách tính chất, đặc điểm nâng cao của class.

I. Kế thừa nhiều classes (Multi-inheritance)

Không như Java — ngôn ngữ chỉ cho kế thừa một class duy nhất cho một class, Python cho phép cho chúng ta kết thừa nhiều class cho một class. Ví dụ:

class Father:
    pass

class Mother:
    pass

class Child(Father, Mother):
    pass

Kết quả của Child.bases trong trường hợp này đó là:

(__main__.Father, __main__.Mother)

II. Tìm attributes với kế thừa

Theo logic, quá trình tìm kiếm attributes như sau:

  • Đầu tiên, tìm kiếm attributes trong local dict
  • Nếu không có, tiếp tục tìm trong class dict
  • Nếu vẫn không có thì tìm ở trong các classes ở mro

Mình sẽ làm rõ ý cuối cùng ở các phần tiếp theo của bài.

III. MRO

MRO là tên viết tắt của Method Resolution Order. Là một chuỗi inheritance mà Python tính toán và lưu nó ở MRO attribute trong class. Như đã nói ở trên khi tìm attributes, Python sẽ đi lần lượt qua các phần tử trong MRO.

Để xem MRO của một class, ta dùng cú pháp ".__mro__". Ví dụ:

def mro(cls: type) -> List[type]:
  """
  Return a list of classes in order corresponding to Python's MRO.
  """
  result = [cls]

  if not cls.__bases__:
      return result
  else:
      return result + _merge(*[mro(kls) for kls in cls.__bases__], cls.__bases__)
0

Output:

def mro(cls: type) -> List[type]:
  """
  Return a list of classes in order corresponding to Python's MRO.
  """
  result = [cls]

  if not cls.__bases__:
      return result
  else:
      return result + _merge(*[mro(kls) for kls in cls.__bases__], cls.__bases__)
1

Python sử dụng cooperative multiple inheritance để quy định một số luật về thứ tự của class:

  • Children are always checked before parents (children classes luôn được kiểm tra trước parents classes) Parents (if multiple) are always checked in the order listed (các parents classes luôn được kiêm tra theo thứ tự được liệt kê)
  • Như ví dụ ở trên, sau khi kiểm tra ở E, class D sẽ được kiểm tra vì D được ghi đầu tiên trong hai base classes. Sau đó, B — parent class của D sẽ được kiểm tra. Cuối cùng, class C và parent của nó - A sẽ được kiểm tra.

Để hiểu rõ hơn, các bạn hãy thử động não, thử giải thích MRO của class E dưới đây:

def mro(cls: type) -> List[type]:
  """
  Return a list of classes in order corresponding to Python's MRO.
  """
  result = [cls]

  if not cls.__bases__:
      return result
  else:
      return result + _merge(*[mro(kls) for kls in cls.__bases__], cls.__bases__)
2

Thuật toán này là có tên là "C3 Linearization Algorithm" nhưng để đơn giản và nhớ hiểu, hãy tưởng tượng đến thứ tự thoát hiểm khi một sự cố như cháy nhà, chìm tàu xảy ra: "Trẻ em đầu tiên, sau đó mới là người lớn" (Children first, followed by parents).

IV. Ví dụ về multi-inheritance

def mro(cls: type) -> List[type]:
  """
  Return a list of classes in order corresponding to Python's MRO.
  """
  result = [cls]

  if not cls.__bases__:
      return result
  else:
      return result + _merge(*[mro(kls) for kls in cls.__bases__], cls.__bases__)
3

def mro(cls: type) -> List[type]:
  """
  Return a list of classes in order corresponding to Python's MRO.
  """
  result = [cls]

  if not cls.__bases__:
      return result
  else:
      return result + _merge(*[mro(kls) for kls in cls.__bases__], cls.__bases__)
4

def mro(cls: type) -> List[type]:
  """
  Return a list of classes in order corresponding to Python's MRO.
  """
  result = [cls]

  if not cls.__bases__:
      return result
  else:
      return result + _merge(*[mro(kls) for kls in cls.__bases__], cls.__bases__)
5

super() sẽ gọi class kế tiếp của class hiện tại trong MRO

Ví dụ:

MRO của NoisyPerson:

def mro(cls: type) -> List[type]:
  """
  Return a list of classes in order corresponding to Python's MRO.
  """
  result = [cls]

  if not cls.__bases__:
      return result
  else:
      return result + _merge(*[mro(kls) for kls in cls.__bases__], cls.__bases__)
6

Thử gọi method talks của NoisyPerson instance:

def mro(cls: type) -> List[type]:
  """
  Return a list of classes in order corresponding to Python's MRO.
  """
  result = [cls]

  if not cls.__bases__:
      return result
  else:
      return result + _merge(*[mro(kls) for kls in cls.__bases__], cls.__bases__)
7

Output:

def mro(cls: type) -> List[type]:
  """
  Return a list of classes in order corresponding to Python's MRO.
  """
  result = [cls]

  if not cls.__bases__:
      return result
  else:
      return result + _merge(*[mro(kls) for kls in cls.__bases__], cls.__bases__)
8

Khi talk() method của girl được gọi, do bản thân NoisyPerson không có method này nên nó sẽ tìm ở class tiếp theo trong MRO là Noisy. Noisy có talk() method sẽ được execute. super().talk() sẽ tìm gọi method talk() của class kế tiếp trong MRO - Person (return 'alo alo'). Đoạn string 'alo alo' này sẽ được upper() và return. Do đó output trả về là "ALO ALO"

Bây giờ nếu ta đổi chỗ 2 parent classes với nhau thì sao?

def mro(cls: type) -> List[type]:
  """
  Return a list of classes in order corresponding to Python's MRO.
  """
  result = [cls]

  if not cls.__bases__:
      return result
  else:
      return result + _merge(*[mro(kls) for kls in cls.__bases__], cls.__bases__)
9

Chạy lại talks method

def _merge(*lists) -> list:
  result: List[Optional[type]] = []
  linearizations = DependencyList(*lists)

  while True:
      if linearizations.exhausted:
          return result

      for head in linearizations.heads:
          if head and (head not in linearizations.tails):  # type: ignore
              result.append(head)
              linearizations.remove(head)

              # Once candidate is found, continue iteration
              # from the first element of the list
              break
      else:
          # Loop never broke, no linearization could possibly be found
          raise ValueError('Cannot compute linearization, a cycle found')
0

Output:

def _merge(*lists) -> list:
  result: List[Optional[type]] = []
  linearizations = DependencyList(*lists)

  while True:
      if linearizations.exhausted:
          return result

      for head in linearizations.heads:
          if head and (head not in linearizations.tails):  # type: ignore
              result.append(head)
              linearizations.remove(head)

              # Once candidate is found, continue iteration
              # from the first element of the list
              break
      else:
          # Loop never broke, no linearization could possibly be found
          raise ValueError('Cannot compute linearization, a cycle found')
1

Đây là MRO của NoisyPerson bây giờ:

def _merge(*lists) -> list:
  result: List[Optional[type]] = []
  linearizations = DependencyList(*lists)

  while True:
      if linearizations.exhausted:
          return result

      for head in linearizations.heads:
          if head and (head not in linearizations.tails):  # type: ignore
              result.append(head)
              linearizations.remove(head)

              # Once candidate is found, continue iteration
              # from the first element of the list
              break
      else:
          # Loop never broke, no linearization could possibly be found
          raise ValueError('Cannot compute linearization, a cycle found')
2

Ta thấy class Person được kiểm tra trước Noisy, trong khi đó Person có method talks() nên nó sẽ được execute.

V. Kết luận

Qua hai phần về chủ đề class, hy vọng các bạn có thêm kiến thức về class và có thể áp dụng nó vào trong công việc của mình. Các bạn hãy đón chờ các bài tiếp theo về chủ đề Python trên tech blog BizFly Cloud nha!

02-05-2019 12:10 | | trong blog | 851 từ | 4 phút để đọc lại: Python Cpython MRO Thuật toán | | in Blog | 851 words | 4 min to read
tags: python cpython mro algorithms

Thứ tự độ phân giải phương pháp (MRO) là một thứ tự trong đó các phương thức nên được kế thừa trong trường hợp nhiều iheritance. Thuật toán tuyến tính hóa C3 là cách MRO hoạt động dưới mui xe kể từ phiên bản 2.3.MRO) is a order in which methods should be inherited in the case of multiple iheritance. C3 linearization algorithm is how MRO works under the hood since version 2.3.

Wikipedia thực hiện một công việc tuyệt vời giải thích thuật toán. Nó có thể được giảm xuống các bước sau & nbsp; các bước:

  1. Tuyến tính hóa (tức là thứ tự độ phân giải) là một lớp và sự hợp nhất của tuyến tính hóa của cha mẹ và một danh sách cha mẹ & nbsp;
  2. Tuyến tính hóa lớp không có phụ huynh bằng với lớp & nbsp;
  3. Quá trình hợp nhất được thực hiện bằng cách chọn người đứng đầu đầu tiên của danh sách không xuất hiện trong đuôi của bất kỳ danh sách nào. Trường hợp đầu là yếu tố đầu tiên của danh sách và đuôi là tất cả nhưng các yếu tố đầu tiên của danh sách. Các đầu được chọn nhiều lần và thêm vào MRO kết quả cho đến khi tất cả các danh sách được & NBSP;MRO until all the lists are exhausted.
  4. Nếu một đầu không thể được chọn trong khi không phải tất cả các danh sách đều không thể tính toán được là không thể tính toán do các thứ tự không nhất quán của các phụ thuộc trong hệ thống phân cấp kế thừa và không có tuyến tính hóa của lớp ban đầu & NBSP;

Hướng dẫn c3 linearization algorithm python - python thuật toán tuyến tính hóa c3

Ví dụ nhiều kế thừa phức tạp, lịch sự H2Power

Xem xét quá trình tuyến tính hóa cho lớp K1:

// first, find the linearizations of K1's parents, L(A), L(B), and L(C),
// and merge them with the parent list [A, B, C]
L(K1) := [K1] + merge(L(A), L(B), L(C), [A, B, C])
// class A is a good candidate for the first merge step, because it only
// appears as the head of the first and last lists
       = [K1] + merge([A, O], [B, O], [C, O], [A, B, C])
// class O is not a good candidate for the next merge step, because it also
// appears in the tails of list 2 and 3; but class B is a good candidate
       = [K1, A] + merge([O], [B, O], [C, O], [B, C])
// class C is a good candidate; class O still appears in the tail of list 3
       = [K1, A, B] + merge([O], [O], [C, O], [C])
// finally, class O is a valid candidate, which also exhausts all remaining lists
       = [K1, A, B, C] + merge([O], [O], [O])
       = [K1, A, B, C, O]

Vì vậy, ở cấp độ cao hơn, việc triển khai ngây thơ của thuật toán tuyến tính hóa C3 có thể được biểu thị dưới dạng đơn giản & nbsp; đệ quy: đệ quy:

def mro(cls: type) -> List[type]:
  """
  Return a list of classes in order corresponding to Python's MRO.
  """
  result = [cls]

  if not cls.__bases__:
      return result
  else:
      return result + _merge(*[mro(kls) for kls in cls.__bases__], cls.__bases__)

Sau đó, _merge liên tục kiểm tra xem danh sách của nó có cạn kiệt và nối các đầu thích hợp vào MRO kết quả:MRO:

def _merge(*lists) -> list:
  result: List[Optional[type]] = []
  linearizations = DependencyList(*lists)

  while True:
      if linearizations.exhausted:
          return result

      for head in linearizations.heads:
          if head and (head not in linearizations.tails):  # type: ignore
              result.append(head)
              linearizations.remove(head)

              # Once candidate is found, continue iteration
              # from the first element of the list
              break
      else:
          # Loop never broke, no linearization could possibly be found
          raise ValueError('Cannot compute linearization, a cycle found')

Để ẩn một số danh sách phụ thuộc nội bộ và sự trừu tượng của danh sách phụ thuộc là & nbsp; được sử dụng:

from collections import deque
from itertools import islice
from typing import List, Tuple, Optional


class Dependency(deque):
    @property
    def head(self) -> Optional[type]:
        try:
            return self[0]
        except IndexError:
            return None

    @property
    def tail(self) -> islice:  # type: ignore
        """
        Return islice object, which is suffice for iteration or calling `in`
        """
        try:
            return islice(self, 1, self.__len__())
        except (ValueError, IndexError):
            return islice([], 0, 0)


class DependencyList:
    """
    A class represents list of linearizations (dependencies)

    The last element of DependencyList is a list of parents.
    It's needed  to the merge process preserves the local
    precedence order of direct parent classes.
    """
    def __init__(self, *lists: Tuple[List[type]]) -> None:
        self._lists = [Dependency(i) for i in lists]

    def __contains__(self, item: type) -> bool:
        """
        Return True if any linearization's tail contains an item
        """
        return any([item in l.tail for l in self._lists])  # type: ignore

    def __len__(self):
        size = len(self._lists)
        return (size - 1) if size else 0

    def __repr__(self):
        return self._lists.__repr__()

    @property
    def heads(self) -> List[Optional[type]]:
        return [h.head for h in self._lists]

    @property
    def tails(self) -> 'DependencyList':  # type: ignore
        """
        Return self so that __contains__ could be called

        Used for readability reasons only
        """
        return self

    @property
    def exhausted(self) -> bool:
        """
        Return True if all elements of the lists are exhausted
        """
        return all(map(lambda x: len(x) == 0, self._lists))

    def remove(self, item: Optional[type]) -> None:
        """
        Remove an item from the lists

        Once an item removed from heads, the leftmost elements of the tails
        get promoted to become the new heads.
        """
        for i in self._lists:
            if i and i.head == item:
                i.popleft()

Toàn bộ cơ sở mã có thể được tìm thấy trong kho lưu trữ C3linear của tôi. Bạn có thể cài đặt nó từ mã nguồn hoặc thông qua & nbsp; pypi:

python setup.py install  # from the source code
pip install c3linear  # from the Cheese Shop

Sau đó, chỉ cần nhập nó và kiểm tra đối với phương pháp MRO của Python đối tượng:

from c3linear.mro import mro

class A: pass
class B(A): pass
mro(B) == B.mro()

Hãy xem các bài kiểm tra để đi sâu vào nhiều kế thừa phức tạp hơn & nbsp; ví dụ.