Python の基礎インタビュー パート 3

1. 現在のディレクトリ内のすべてのファイル名を取得します

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

import os

def get_all_files(directory):

    file_list = []<br>    # <code>os.walk</code>返回一个生成器,每次迭代时返回当前目录路径、子目录列表和文件列表

    for root, dirs, files in os.walk(directory):

        for file in files:

            file_list.append(os.path.join(root, file))

    return file_list

# 获取当前目录下的所有文件名

current_directory = os.getcwd()

files = get_all_files(current_directory)

# 打印所有文件名

for file in files:

    print(file)

2. Python のジェネレーターとイテレーターの違い

Iterator は反復プロトコルを実装するオブジェクトであり、メソッド__iter__()__next__()メソッドを提供する必要があります。__iter__()メソッドを呼び出すことによってイテレータはそれ自体を返すことができ、__next__()メソッドを呼び出すことによってイテレータは、StopIteration要素がなくなって例外がスローされるまで、順番に次の要素を返すことができます。イテレータは、必要なときに 1 つの要素のみを生成する遅延計算方法であり、それによってメモリ領域を節約します。反復可能なオブジェクトは、関数を使用してiter()イテレータに変換できます。

ジェネレーターは、より簡潔な構文を使用してイテレーターを定義する特別なタイプのイテレーターです。ジェネレーターはyield関数内のキーワードを通じて実装できます。関数がyieldステートメントを実行すると、実行を一時停止して値を返し、現在の状態を保存し、次回呼び出されたときに最後に一時停止した位置から実行を継続します。ジェネレーター関数は、通常の関数と同様にパラメーターを受け取ることができ、ループや条件文などのロジックを含めることができます。ジェネレーターは、イテレーターを実装するための非常に便利で効率的な方法です。

ジェネレータとイテレータの違いの概要は次のとおりです。

  1. 構文: ジェネレーターはyieldキーワードを使用して定義されますが、イテレーターには実装__iter__()__next__()メソッドが必要です。
  2. 実装: ジェネレーターは関数を使用して定義でき、イテレーターはクラスによって実装できます。
  3. 状態の保存: ジェネレーターはyieldステートメントで実行を一時停止して現在の状態を保存し、次回呼び出されたときに最後に一時停止した位置から実行を再開します。イテレーターは内部状態とポインターを通じて反復位置を保存します。
  4. 単純さ: ジェネレーターの構文はより簡潔で、通常の関数定義と制御フロー ステートメントを使用できます。反復子は複数の特別なメソッドを実装する必要があり、コードは比較的大きくなります。
  5. 遅延計算: ジェネレーターは遅延計算され、毎回必要なときに 1 つの要素のみを生成します。イテレーターは遅延計算を実装することもできますが、手動制御が必要です。

つまり、ジェネレーターは、より簡潔で便利な構文を提供する特別な種類のイテレーターです。ジェネレーターはyield関数内のステートメントを通じて反復処理を実装でき、ロジックは通常の関数と同様に記述できます。イテレータはクラスを通じて実装できるより一般的な概念であり、明示的な定義__iter__()__next__()メソッドが必要です。ジェネレーターであってもイテレーターであっても、オンデマンドで大量のデータを生成および処理する機能を実現し、コードの効率と可読性を向上させることができます。

 栗少々:

大規模なデータ セットを反復処理する必要がある場合、ジェネレーターを使用すると、データ セット全体を一度にメモリにロードするのではなく、オンデマンドでデータを生成できます。

以下は、ジェネレーターを使用してフィボナッチ数列の最初の n 要素をオンデマンドで生成する簡単な例です。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

def fibonacci_generator(n):

    a, b = 01

    count = 0

    while count < n:

        yield a

        a, b = b, a + b

        count += 1

# 使用生成器按需生成斐波那契数列的前10个元素

fibonacci = fibonacci_generator(10)

# 逐个打印生成的元素

for num in fibonacci:

    print(num)

fibonacci_generator上記のコードでは、ステートメントを使用してyieldフィボナッチ数列の要素を生成する    ジェネレーター関数を定義します。ジェネレーターのメソッドが呼び出されるたびに、ステートメント__next__()が実行されます。yield

現在のフィボナッチ数を返し、実行を一時停止して、現在の状態を保存します。その後、次にジェネレータの__next__()メソッドが呼び出されたときに、最後に一時停止した場所から実行を継続し、次のフィボナッチ数を生成します。このようにして、ジェネレーターを反復処理できます。

オンデマンドでフィボナッチ数列の要素を取得します。ジェネレーター オブジェクトを反復処理すると、フィボナッチ数列の要素が生成され、出力されます。ジェネレーターはオンデマンドでデータを生成するため、要素全体を一度に生成するのではなく、必要なときにのみ要素を生成します。

数字の並び。これにより、特にフィボナッチ数列が大きい場合にメモリ領域が節約されます。要約すると、ジェネレーターは、オンデマンドでデータを生成し、メモリ領域を節約し、簡潔で便利な方法を提供できる特別な関数とみなすことができます。

イテレータを実装します。ジェネレーターを使用すると、大量のデータを一度にメモリーにロードすることを回避し、必要に応じてデータを 1 つずつ生成できるため、コードの効率とスケーラビリティーが向上します。

3. 反復可能オブジェクトとは何ですか?またその原理は何ですか?

  反復可能オブジェクト (Iterable) は、反復的に走査できるオブジェクトを指します。多くのプログラミング言語では、反復とは、コレクションの要素に特定の順序で 1 つずつアクセスするプロセスを指します。Python では、反復可能オブジェクトとは、Iterator プロトコルを実装するオブジェクトを指します。

イテレータ プロトコルには 2 つのメソッドが含まれています。

  1. __iter__() メソッド: このメソッドはイテレータ オブジェクトを返します。Iterator オブジェクトは、特定の反復ロジックを実装するために使用され、__next__() メソッドを含む必要があります。

  2. __next__() メソッド: このメソッドは、反復子の次の要素を返します。返す要素がない場合は、StopIteration 例外が発生します。

iterable オブジェクトを使用して反復処理を行う場合、実際には iterator オブジェクトを通じて実行されます。iterator オブジェクトは、現在の反復状態を追跡し、次の要素を提供する役割を果たします。iterator オブジェクトは、反復ごとに __next__() メソッドを呼び出し、すべての要素が走査されるか StopIteration 例外が発生するまで、次の要素を返します。

Python の多くの組み込みデータ型とコンテナーは、List、Tuple、Dictionary、Set などの反復可能なオブジェクトです。さらに、カスタム クラスを使用して反復可能オブジェクトを実装することもできます。クラス内で __iter__() メソッドを定義し、このメソッドで反復子オブジェクトを返すだけです。

以下は、iterable オブジェクトと iterator オブジェクトを使用して反復処理を行う方法を示す例です。

1

2

3

4

5

6

7

8

9

10

11

12

13

# 创建一个可迭代对象

my_list = [12345]

# 获取迭代器对象

my_iter = iter(my_list)

# 使用迭代器对象进行迭代

try:

    while True:

        item = next(my_iter)

        print(item)

except StopIteration:

    pass

  上の例では、 function を呼び出してイテレータ オブジェクトiter()を取得し、次にその関数を使用してイテレータ オブジェクトから次の要素を取得し、すべての要素が走査されるか、my_listmy_iternext()

StopIteration異常になるまで。反復可能オブジェクトの原理は反復子プロトコルの実装に基づいており、シーケンス内の次の要素は反復子オブジェクトの __next__() メソッドを通じて提供されます。このメカニズムにより、コレクション内の要素を簡単に比較できます。

1 つずつアクセスして処理し、統一された反復インターフェイスを提供します

反復可能なオブジェクトを自分で実装する小さな例

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

class MyIterable:

    def __init__(self, data):

        self.data = data

    def __iter__(self):

        self.index = 0

        return self

    def __next__(self):

        if self.index < len(self.data):

            item = self.data[self.index]

            self.index += 1

            return item

        else:

            raise StopIteration

# 创建一个可迭代对象实例

my_iterable = MyIterable([12345])

# 使用迭代器进行迭代

for item in my_iterable:

    print(item)

4. Python2 と Python3 の主な違いは何ですか:

Python 2.x と Python 3.x の主な違いは次のとおりです。

  1. Print 関数: Python 2.x では、print ステートメントはキーワードであり、 print "Hello" に似た構文を使用します。Python 3.x では、print これは組み込み関数に変換され、かっこを使用する必要があります (例: )  print("Hello")

  2. 整数の除算: Python 2.x では、整数の除算の結果は整数に切り捨てられ ます5 / 2 。 2Python 3.x では、整数除算の結果は小数部分を保持し、浮動小数点の結果を取得します。たとえば、 5 / 2 結果は です 2.5切り捨てられた整数の除算を実行する場合は、 // 演算子を使用できます。

  3. Unicode 文字列: Python 2.x では、文字列型が通常の文字列と Unicode 文字列 ( u 接頭辞で表される) に分割されているため、文字列の処理に混乱が生じます。Python 3.x では、すべての文字列は Unicode 文字列であり、通常の文字列はバイトで表され、 b 接頭辞で表す必要があります。

  4. xrange 替代 range:在 Python 2.x 中,range 函数返回的是一个列表,如果需要生成大范围的整数序列,会占用大量内存。而在 Python 3.x 中,range 函数的实现类似于 Python 2.x 中的 xrange,返回一个迭代器对象,节省了内存。

  5. 异常语法:在 Python 2.x 中,捕获异常时使用的语法是 except Exception, e,将异常对象存储在变量 e 中。而在 Python 3.x 中,使用 except Exception as e 的语法,将异常对象存储在变量 e 中。

  6. input 函数:在 Python 2.x 中,input 函数会将用户输入的内容作为 Python 代码进行解析,存在安全风险。而在 Python 3.x 中,input 函数始终将用户输入的内容作为字符串返回,不进行解析。

除了上述主要区别之外,Python 3.x 还进行了一些其他改进,包括改进的类定义语法、更好的模块管理和导入机制、更一致的异常处理和错误机制等。然而,这也导致了 Python 2.x 代码无法直接在 Python 3.x 中运行,需要进行一些修改和调整。

5. 说说Python中多线程和多进程

  1. 多线程(Multithreading):

    • 多线程是指在一个进程内创建多个线程,每个线程独立执行不同的任务。多线程共享进程的内存空间,因此线程之间可以方便地共享数据。
    • 在 Python 中,可以使用 threading 模块来创建和管理线程。通过创建 Thread 类的实例,指定要执行的函数或方法,并调用 start() 方法,可以启动一个线程。
    • Python 的多线程由于全局解释器锁(Global Interpreter Lock,GIL)的存在,同一时刻只允许一个线程执行 Python 字节码。这意味着多线程并不能充分利用多核处理器,并发性能受限。多线程适用于 I/O 密集型任务,如网络请求、文件读写等,但对于 CPU 密集型任务,多线程并不能提升性能。
  2. 多进程(Multiprocessing):

    • 多进程是指创建多个独立的进程,每个进程都有自己的内存空间和系统资源。多个进程之间相互独立,可以并行执行不同的任务。每个进程都有自己的 Python 解释器,因此可以充分利用多核处理器,提高并发性能。
    • 在 Python 中,可以使用 multiprocessing 模块来创建和管理进程。通过创建 Process 类的实例,指定要执行的函数或方法,并调用 start() 方法,可以启动一个进程。
    • 多进程可以通过进程间通信(Inter-Process Communication,IPC)来实现进程之间的数据共享。Python 提供了多种 IPC 机制,如队列(Queue)、管道(Pipe)和共享内存等。

总结:

  • 多线程适合处理 I/O 密集型任务,可以提高程序的响应能力和效率。
  • 多进程适合处理 CPU 密集型任务,可以充分利用多核处理器提高并发性能。
  • 在 Python 中,多线程受到 GIL 的限制,多进程可以绕过 GIL,实现真正的并行执行。
  • 使用多线程或多进程时需要注意线程安全和进程安全的问题,避免数据竞争和共享资源的冲突。

6. Python中GIL锁:

  全局解释器锁(Global Interpreter Lock,简称 GIL)是在 CPython 解释器中存在的一个特性。它是一种机制,用于保证同一时刻只有一个线程执行 Python 字节码。虽然 GIL 的存在确保了 CPython 解释器的线程安全性,但也对多线程并发执行带来了一些限制。

以下是对 GIL 的一些详细解释和理解:

  1. GIL 的作用:

    • GIL 的主要作用是保护 CPython 解释器内部的数据结构免受并发访问的影响,确保线程安全。
    • CPython 使用引用计数(Reference Counting)作为内存管理的主要机制。GIL 保证了在修改引用计数时的原子性,避免了竞态条件(Race Condition)和内存泄漏问题。
    • GIL 还可以简化 CPython 解释器的实现,使其更加简单高效。
  2. GIL 的影响:

    • 由于 GIL 的存在,同一时刻只有一个线程可以执行 Python 字节码,其他线程被阻塞。这意味着多线程并不能充分利用多核处理器,无法实现真正的并行执行。
    • 对于 CPU 密集型任务,由于 GIL 的限制,多线程并不能提升性能。实际上,由于线程切换的开销,可能导致多线程执行速度比单线程还要慢。
    • GIL 对于 I/O 密集型任务的影响相对较小,因为线程在进行 I/O 操作时会主动释放 GIL,允许其他线程执行。因此,多线程在处理 I/O 操作时仍然可以提供一定的性能优势。
  3. 解决 GIL 的方法:

    • 采用多进程:由于每个进程都有独立的 Python 解释器和 GIL,多进程可以充分利用多核处理器,实现并行执行。
    • 使用扩展模块:某些扩展模块,如 NumPy、Pandas 等,使用 C/C++ 编写,可以释放 GIL,允许多线程并行执行。
    • 使用多线程库:一些第三方库,如 multiprocessing 模块、concurrent.futures 模块等,提供了替代方案,使得在某些情况下可以绕过 GIL 的限制。

需要注意的是,GIL 只存在于 CPython 解释器中,而其他实现(如 Jython、IronPython)可能没有 GIL。此外,对于许多类型的应用程序,如 I/O 密集型、并发处理不频繁的应用程序,GIL

的影响较小,可以继续使用多线程来实现并发。然而,对于 CPU 密集型任务和需要充分利用多核处理器的应用程序,考虑使用多进程或其他解决方案来规避 GIL。

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!

おすすめ

転載: blog.csdn.net/NHB456789/article/details/135221348