Pencocokan pola. Sekarang dengan Python

Hei!



Pencocokan pola akhirnya dibawa ke anniversary minor dari python ketiga. Konsepnya sendiri hampir tidak bisa disebut baru, sudah diimplementasikan dalam banyak bahasa, baik generasi baru (Rust, Golang) maupun yang sudah lebih dari 0x18 (Java).





Pencocokan pola diumumkan oleh Guido van Rossum , penulis bahasa pemrograman Python dan "diktator seumur hidup yang murah hati".



Nama saya Denis Kaishev, dan saya adalah peninjau kode untuk kursus pengembang Python Tengah . Dalam posting ini saya ingin memberi tahu Anda mengapa Python memiliki pencocokan pola dan bagaimana cara menggunakannya.



Secara sintaksis, pencocokan pola pada dasarnya sama dengan beberapa bahasa lain:



match_expr:
    | star_named_expression ',' star_named_expressions?
    | named_expression
match_stmt: "match" match_expr ':' NEWLINE INDENT case_block+ DEDENT
case_block: "case" patterns [guard] ':' block
guard: 'if' named_expression
patterns: value_pattern ',' [values_pattern] | pattern
pattern: walrus_pattern | or_pattern
walrus_pattern: NAME ':=' or_pattern
or_pattern: '|'.closed_pattern+
closed_pattern:
    | capture_pattern
    | literal_pattern
    | constant_pattern
    | group_pattern
    | sequence_pattern
    | mapping_pattern
    | class_pattern
capture_pattern: NAME !('.' | '(' | '=')
literal_pattern:
    | signed_number !('+' | '-')
    | signed_number '+' NUMBER
    | signed_number '-' NUMBER
    | strings
    | 'None'
    | 'True'
    | 'False'
constant_pattern: attr !('.' | '(' | '=')
group_pattern: '(' patterns ')'
sequence_pattern: '[' [values_pattern] ']' | '(' ')'
mapping_pattern: '{' items_pattern? '}'
class_pattern:
    | name_or_attr '(' ')'
    | name_or_attr '(' ','.pattern+ ','? ')'
    | name_or_attr '(' ','.keyword_pattern+ ','? ')'
    | name_or_attr '(' ','.pattern+ ',' ','.keyword_pattern+ ','? ')'
signed_number: NUMBER | '-' NUMBER
attr: name_or_attr '.' NAME
name_or_attr: attr | NAME
values_pattern: ','.value_pattern+ ','?
items_pattern: ','.key_value_pattern+ ','?
keyword_pattern: NAME '=' or_pattern
value_pattern: '*' capture_pattern | pattern
key_value_pattern:
    | (literal_pattern | constant_pattern) ':' or_pattern
    | '**' capture_pattern

      
      





Ini mungkin tampak rumit dan membingungkan, tetapi pada kenyataannya semuanya bermuara pada sesuatu seperti ini:



match some_expression:
    case pattern_1:
        ...
    case pattern_2:
        ...

      
      





Terlihat jauh lebih jelas dan lebih enak dipandang.



Templatenya sendiri dibagi menjadi beberapa grup:



  • Pola Literal;
  • Tangkap Pola;
  • Pola Wildcard;
  • Pola Nilai Konstan;
  • Pola Urutan;
  • Pola Pemetaan;
  • Pola Kelas.


Saya akan memberi tahu Anda sedikit tentang masing-masing.



Pola Literal



Pola literal, seperti namanya, melibatkan pencocokan serangkaian nilai, yaitu string, bilangan, boolean, dan NULL None.



Sepertinya string == 'string'



metode ini sedang digunakan __eq__



.



match number:
    case 42:
        print('answer')
    case 43:
        print('not answer')

      
      





Tangkap Pola



Template pengambilan memungkinkan Anda untuk mengikat variabel dengan nama yang diberikan dalam template dan menggunakan nama itu dalam lingkup lokal.



match greeting:
    case "":
        print('Hello my friend')
    case name:
        print(f'Hello  {name}')
      
      







Pola karakter pengganti



Jika ada terlalu banyak opsi pencocokan, Anda dapat menggunakan _



, yang merupakan nilai default tertentu dan akan cocok dengan semua elemen dalam struktur match






match number:
    case 42:
        print("Its’s forty two")
    case _:
        print("I don’t know, what it is")
      
      





Pola Nilai Konstan



Saat menggunakan konstanta, Anda perlu menggunakan nama titik-titik, misalnya enumerasi, jika tidak, pola pengambilan akan berfungsi.



OK = 200
CONFLICT = 409

response = {'status': 409, 'msg': 'database error'}
match response['status'], response['msg']:
    case OK, ok_msg:
        print('handler 200')
    case CONFLICT, err_msg:
        print('handler 409')
    case _:
        print('idk this status')
      
      





Dan hasil yang diharapkan bukanlah yang paling jelas.



Pola Urutan



Hal ini memungkinkan Anda untuk membandingkan daftar, tupel, dan benda lainnya dari collections.abc.Sequence



, kecuali str



, bytes



, bytearray



.



answer = [42]
match answer:
    case []:   
        print('i do not find answer')
    case [x]:
        print('asnwer is 42')
    case [x, *_]:
        print('i find more than one answers')
      
      





Sekarang tidak perlu menelepon setiap kali len()



untuk memeriksa jumlah item dalam daftar, karena metode tersebut akan dipanggil __len__



.



Pola Pemetaan



Grup ini agak mirip dengan grup sebelumnya, hanya di sini kami mencocokkan kamus, atau, tepatnya, objek bertipe collections.abc.Mapping



. Mereka dapat digabungkan dengan cukup baik satu sama lain.



args = (1, 2)
kwargs = {'kwarg': 'kwarg', 'one_more_kwarg': 'one_more_kwarg'}

def match_something(*args, **kwargs):
    match (args, kwargs):
        case (arg1, arg2), {'kwarg': kwarg}:
            print('i find positional args and one keyword args')
        case (arg1, arg2), {'kwarg': kwarg, 'one_more_kwarg': one_more_kwarg}:
            print('i find a few keyword args')
        case _:
            print('i cannot match anything')

match_something(*args, **kwargs)
      
      





Dan semuanya akan baik-baik saja, tetapi ada fitur. Pola ini menjamin masuknya kunci-kunci ini ke dalam kamus, tetapi panjang kamus tidak menjadi masalah. Jadi saya menemukan argumen posisi dan satu kata kunci args akan muncul di layar .



Pola kelas



Untuk tipe data yang ditentukan pengguna, sintaksnya mirip dengan inisialisasi objek.



Beginilah tampilannya dengan contoh kelas data:



from dataclasses import dataclass

@dataclass
class Coordinate:
    x: int
    y: int
    z: int

coordinate = Coordinate(1, 2, 3)
match coordinate:
    case Coordinate(0, 0, 0):
        print('Zero point')
    case _:
        print('Another point')

      
      





Anda juga bisa menggunakan if



, atau disebut guard



. Jika kondisinya salah, maka pencocokan pola akan dilanjutkan. Perlu dicatat bahwa polanya dicocokkan terlebih dahulu, dan baru setelah itu kondisinya dicentang:



case Coordinate(x, y, z) if z == 0:
    print('Point in the plane XY')
      
      





Jika Anda menggunakan kelas secara langsung, maka Anda memerlukan atribut __match_args__



di mana argumen posisi diperlukan (untuk nametuple dan dataclass, itu __match_args__



dibuat secara otomatis).



class Coordinate:
    __match_args__ = ['x', 'y', 'z']

    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

oordinate = oordinate(1, 2, 3)
match oordinate:
    case oordinate(0, 0, 0):
        print('Zero oordinate')
    case oordinate(x, y, z) if z == 0:
        print('oordinate in the plane Z')
    case _:
        print('Another oordinate')

      
      





Jika tidak, pengecualian TypeError akan dilemparkan: Koordinat () menerima 0 sub-pola posisi (3 diberikan)



Apa intinya?



Faktanya, itu terlihat seperti gula sintaksis lain bersama dengan yang baru-baru ini walrus operator



. Implementasinya , sebagaimana berdiri, mengubah blok pernyataan match



menjadi konstruksi yang setara if/else



, yaitu bytecode, yang memiliki efek yang sama.





Armin Ronacher, pencipta kerangka web Flask untuk Python, dengan sangat ringkas mendeskripsikan status pencocokan Pola saat ini.



Ya, sulit untuk membantah: kode akan menjadi agak lebih bersih daripada if/else



sepertiga dari menara layar. Tapi Anda juga tidak bisa menyebutnya sebagai sesuatu yang menghasilkan efek wow. Tidak buruk bahwa ini diperkenalkan: akan mudah digunakan di beberapa tempat, tetapi tidak di semua tempat. Dengan satu atau lain cara, hal utama dengan kebaruan ini adalah tidak berlebihan, tidak berjalan lebih cepat untuk memperbarui semua proyek ke 3.10 dan menulis ulang semuanya, karena:

Sekarang lebih baik daripada tidak sama sekali. Meskipun tidak pernah lebih baik dari sekarang.


Apakah Anda akan menggunakannya? Jika ya, dimana?



All Articles