Rumah >pembangunan bahagian belakang >Tutorial Python >Penjelasan terperinci tentang penjana python dalam satu artikel

Penjelasan terperinci tentang penjana python dalam satu artikel

WBOY
WBOYke hadapan
2022-06-09 16:02:172689semak imbas

Artikel ini membawa anda pengetahuan yang berkaitan tentang python, yang terutamanya memperkenalkan isu berkaitan penjana, termasuk konsep penjana, proses pelaksanaan penjana, hasil dan penjanaan kaedah Kontena dan kandungan lain, mari kita ambil lihat mereka di bawah saya harap ia akan membantu semua orang.

Penjelasan terperinci tentang penjana python dalam satu artikel

Pembelajaran yang disyorkan: tutorial video python

Artikel ini membawa anda pengetahuan yang berkaitan tentang Python, termasuk pengenalan utama Kami telah membincangkan beberapa isu berkaitan tentang penjana, termasuk konsep penjana, proses pelaksanaan penjana, kaedah hasil dan penjana, dan lain-lain. Mari kita lihat bersama-sama saya harap ia akan membantu semua orang.

1. Konsep Generator

Generator (Bahasa Inggeris: generator) adalah perkara yang sangat menarik dan sering dianggap sebagai kemahiran pengaturcaraan lanjutan dalam Python. Walau bagaimanapun, saya masih

gembira untuk membincangkan topik ini dengan pembaca di sini - walaupun anda mungkin seorang pemula - kerana saya percaya bahawa tujuan membaca tutorial ini bukan untuk menghadkan diri anda kepada tahap Pemula, anda mesti mempunyai hati yang tidak terkawal - untuk menjadi tuan Python. Jadi, mari kita mula belajar tentang penjana.

Ingat "iterator" dalam bahagian sebelumnya? Penjana dan iterator mempunyai hubungan asal tertentu. Penjana mestilah boleh diulang. Memang benar ia bukan sahaja pengulangan

, tetapi selain itu, ia tidak mempunyai banyak kegunaan lain, jadi kita boleh memahaminya sebagai lelaran Define automatik yang sangat mudah.

2. Penjana ringkas

>>> my_generator = (x*x for x in range(4))

Adakah ini sangat serupa dengan analisis senarai? Perhatikan dengan teliti, ia bukan senarai Jika anda mendapatnya seperti ini, ia adalah senarai:

>>> my_list = [x*x for x in range(4)]

Perbezaan antara dua di atas ialah [] atau (). hasilnya berbeza sama sekali.

>>> dir(my_generator)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__',
'__iter__',
'__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running',
'next',
'send', 'throw']

Untuk pemerhatian yang lebih mudah, saya telah menyusun semula keputusan di atas. Adakah anda telah menemui kaedah yang diperlukan __inter__() dan seterusnya() dalam iterator, yang menunjukkan bahawa ia adalah iterator. Jika ia adalah lelaran, anda boleh menggunakan gelung for untuk membacakan nilainya mengikut urutan

>>> for i in my_generator:
... print i
...
0
1
4
9
>>> for i in my_generator:
... print i
...

Semasa gelung pertama, nilai dalam penjana_saya dibaca dan dicetak mengikut turutan . Namun, apabila membacanya semula Apabila saya melakukannya, saya mendapati tiada hasil. Ciri ini juga adalah apa yang dimiliki oleh iterator.

Untuk senarai itu, ia berbeza:

>>> for i in my_list:
... print i
...
0
1
4
9
>>> for i in my_list:
... print i
...
0
1
4
9

Adakah cukup bahawa penjana hanya menggantikan [] dalam penghuraian senarai dengan ()? Ini hanyalah satu bentuk ungkapan dan penggunaan penjana Mengikuti penamaan penghuraian senarai

, ia boleh dipanggil "penghuraian penjana" (atau: terbitan penjana, ungkapan penjana ).

Ekspresi penghuraian penjana mempunyai banyak kegunaan dan merupakan pilihan yang baik untuk menggantikan senarai di banyak tempat. Terutamanya apabila berurusan dengan sejumlah besar nilai, seperti yang dinyatakan dalam bahagian sebelumnya, senarai menduduki lebih banyak memori Kelebihan iterator (penjana adalah iterator) ialah mereka menduduki kurang memori, jadi tidak perlu membuat instantiate penjana (atau iterator. ) Tukarkannya menjadi senarai dan kendalikannya terus untuk menunjukkan kelebihan lelaran. Contohnya:

>>> sum(i*i for i in range(10))
285

Beri perhatian kepada operasi jumlah() di atas Jangan fikir ada tanda kurung yang hilang, ia hanya ditulis seperti ini. menawan tak? Jika senarai itu, anda

perlu:

>>> sum([i*i for i in range(10)])
285

Penjana yang diperolehi oleh analisis penjana meliputi beberapa butiran penjana, dan kawasan yang berkenaan adalah terhad. Seterusnya, kami akan menganalisis dalaman penjana dan mendapatkan pemahaman yang lebih mendalam tentang alat ajaib ini.

3. Definisi dan proses pelaksanaan

Perkataan hasil bermaksud "pengeluaran, pengeluaran" dalam bahasa Cina, ia digunakan sebagai kata kunci (anda menggunakannya dalam pembolehubah, fungsi, kelas

tidak boleh digunakan dalam nama), yang merupakan simbol penjana.

>>> def g():
... yield 0
... yield 1
... yield 2
...
>>> g
<function g at 0xb71f3b8c>

Mencipta fungsi yang sangat mudah Satu-satunya perbezaan daripada fungsi yang kita lihat sebelum ini ialah penggunaan tiga pernyataan hasil. Kemudian lakukan operasi berikut:

>>> ge = g()
>>> ge
<generator object g at 0xb7200edc>
>>> type(ge)
<type &#39;generator&#39;>

Nilai pulangan bagi fungsi yang dicipta di atas ialah objek jenis penjana.

>>> dir(ge)
['__class__', '__delattr__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__iter__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'close', 'gi_code', 'gi_frame', 'gi_running', 'next', 'send', 'throw']

Saya melihat __iter__() dan seterusnya() di sini, menunjukkan bahawa ia adalah lelaran. Dalam kes ini, sudah tentu:

>>> ge.next()
0
>>> ge.next()
1
>>> ge.next()
2
>>> ge.next()
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
StopIteration

Seperti yang anda boleh lihat daripada contoh mudah ini, nilai pulangan bagi fungsi yang mengandungi kata kunci hasil ialah objek jenis penjana, dan objek penjana ini ialah lelaran.

Kami memanggil fungsi yang mengandungi penjana kenyataan hasil. Penjana ialah iterator yang ditakrifkan dengan sintaks fungsi biasa. Seperti yang dapat dilihat daripada contoh di atas, penjana ini (juga pengulangan) tidak menulis __inter__() dan seterusnya() seperti iterator dalam bahagian sebelumnya semasa proses takrifan Sebaliknya, selagi penyataan hasil digunakan, the fungsi biasa Ia secara ajaib menjadi penjana dan mempunyai ciri-ciri fungsi lelaran. Fungsi

penyata hasil adalah untuk mengembalikan nilai yang sepadan apabila dipanggil. Mari analisa proses berjalan di atas secara terperinci:

1 ge = g(): Kecuali untuk mengembalikan penjana, tiada operasi dan tiada nilai dikembalikan.

2. ge.next(): Tidak sampai masa ini penjana mula melaksanakan Apabila ia menemui penyataan hasil pertama, ia mengembalikan nilai dan menjeda pelaksanaan (sesetengah memanggilnya

.

为挂起)。

3. ge.next() :从上次暂停的位置开始,继续向下执行,遇到 yield 语句,将值返回,又暂停。

4. gen.next() :重复上面的操作。

5. gene.next() :从上面的挂起位置开始,但是后面没有可执行的了,于是 next() 发出异常。

从上面的执行过程中,发现 yield 除了作为生成器的标志之外,还有一个功能就是返回值。那么它跟 return 这个返回值有什么区别呢?

4. yield

为了弄清楚 yield 和 return 的区别,我写了两个函数来掩饰:

>>> def r_return(n):
... print "You taked me."
... while n > 0:
... print "before return"
... return n
... n -= 1
... print "after return"
...
>>> rr = r_return(3)
You taked me.
before return
>>> rr
3

从函数被调用的过程可以清晰看出, rr = r_return(3) ,函数体内的语句就开始执行了,遇到 return,将值返

回,然后就结束函数体内的执行。所以 return 后面的语句根本没有执行。这是 return 的特点

下面将 return 改为 yield:

>>> def y_yield(n):
... print "You taked me."
... while n > 0:
...     print "before yield"
...     yield n
...     n -= 1
...     print "after yield"
...
>>> yy = y_yield(3) #没有执行函数体内语句
>>> yy.next() #开始执行
You taked me.
before yield
3 #遇到 yield,返回值,并暂停
>>> yy.next() #从上次暂停位置开始继续执行
after yield
before yield
2 #又遇到 yield,返回值,并暂停
>>> yy.next() #重复上述过程
after yield
before yield
1
>>> yy.next()
after yield #没有满足条件的值,抛出异常
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
StopIteration

结合注释和前面对执行过程的分析,读者一定能理解 yield 的特点了,也深知与 return 的区别了。

一般的函数,都是止于 return。作为生成器的函数,由于有了 yield,则会遇到它挂起,如果还有 return,遇到它就直接抛出 SoptIteration 异常而中止迭代。

#!/usr/bin/env Python
# coding=utf-8

def fibs(max):
    """
    斐波那契数列的生成器
    """
    n, a, b = 0, 0, 1
    while n < max:
        yield b
        a, b = b, a + b
        n = n + 1
if __name__ == "__main__":
    f = fibs(10)
    for i in f:
        print i ,

运行结果如下:

$ python 21501.py
1 1 2 3 5 8 13 21 34 55

用生成器方式实现的斐波那契数列是不是跟以前的有所不同了呢?大家可以将本教程中已经演示过的斐波那契数列实现方式做一下对比,体会各种方法的差异。

经过上面的各种例子,已经明确,一个函数中,只要包含了 yield 语句,它就是生成器,也是迭代器。这种方式显然比前面写迭代器的类要简便多了。但,并不意味着上节的就被抛弃。是生成器还是迭代器,都是根据具体的使用情景而定。

5. 生成器方法

在 python2.5 以后,生成器有了一个新特征,就是在开始运行后能够为生成器提供新的值。这就好似生成器

和“外界”之间进行数据交流。

>>> def repeater(n):
... while True:
...     n = (yield n)
...
>>> r = repeater(4)
>>> r.next()
4
>>> r.send("hello")
'hello

当执行到 r.next() 的时候,生成器开始执行,在内部遇到了 yield n 挂起。注意在生成器函数中, n = (yield

n) 中的 yield n 是一个表达式,并将结果赋值给 n,虽然不严格要求它必须用圆括号包裹,但是一般情况都这

么做,请大家也追随这个习惯。

当执行 r.send("hello") 的时候,原来已经被挂起的生成器(函数)又被唤醒,开始执行 n = (yield n) ,也就是

讲 send() 方法发送的值返回。这就是在运行后能够为生成器提供值的含义。

如果接下来再执行 r.next() 会怎样?

>>> r.next()

什么也没有,其实就是返回了 None。按照前面的叙述,读者可以看到,这次执行 r.next() ,由于没有传入任何值,yield 返回的就只能是 None.

还要注意,send() 方法必须在生成器运行后并挂起才能使用,也就是 yield 至少被执行一次。如果不是这样:

>>> s = repeater(5)
>>> s.send("how")
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
TypeError: can't send non-None value to a just-started generator

就报错了。但是,可将参数设为 None:

>>> s.send(None)
5

这是返回的是调用函数的时传入的值。

此外,还有两个方法:close() 和 throw()

• throw(type, value=None, traceback=None):用于在生成器内部(生成器的当前挂起处,或未启动时在定

义处)抛出一个异常(在 yield 表达式中)。

• close():调用时不用参数,用于关闭生成器。

推荐学习:python视频教程

Atas ialah kandungan terperinci Penjelasan terperinci tentang penjana python dalam satu artikel. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:csdn.net. Jika ada pelanggaran, sila hubungi admin@php.cn Padam