python 학습 요점 (2)

11487 단어
내 블로그:https://www.luozhiyun.com/archives/269

'==' VS 'is'


'=='조작부호는 대상 간의 값이 같은지 비교합니다.is'조작부호는 대상의 신분 표지가 같은지, 즉 같은 대상인지, 같은 메모리 주소를 가리키는지 비교한다.
예:
a = 10
b = 10
 
a == b
True
 
id(a)
4427562448
 
id(b)
4427562448
 
a is b
True

파이톤은 10이라는 값에 메모리를 열고 변수 a와 b가 동시에 이 메모리 구역을 가리킨다. 즉, a와 b는 모두 10이라는 변수를 가리키기 때문에 a와 b의 값은 같고 id도 같다.
다만, 정형 숫자의 경우 위 a is b가 트루라는 결론은 -5에서 256 범위의 숫자에만 적용된다.여기는 자바의 Integer 캐시와 약간 비슷합니다. 자바 캐시 - 127에서 128입니다.
하나의 변수와 하나의 단일 예 (singleton) 를 비교할 때, 보통 'is' 를 사용합니다.대표적인 예는 변수가 None인지 확인하는 것입니다.
if a is None:
      ...
 
if a is not None:
      ...

조작부호'is'의 속도 효율을 비교하면 통상적으로'=='보다 우수하다.'is'조작부호는 다시 불러올 수 없기 때문에 a==b를 실행하면 a.eq(b)를 실행하는 것과 같고 파이톤의 대부분 데이터 형식은 다시 불러옵니다eq__이 함수.

얕은 복사와 깊이 복사


얕은 복사


얕은 복사란 메모리를 다시 분배하여 새로운 대상을 만드는 것을 가리키며 그 안의 요소는 원래 대상의 중성자 대상의 인용이다.따라서 원 대상 중의 원소가 변할 수 없다면 상관없다.그러나 요소가 변경될 경우 얕은 복사본은 다음과 같은 부작용을 낳습니다.
l1 = [[1, 2], (30, 40)]
l2 = list(l1)
l1.append(100)
l1[0].append(3)
 
l1
[[1, 2, 3], (30, 40), 100]
 
l2
[[1, 2, 3], (30, 40)]
 
l1[1] += (50, 60)
l1
[[1, 2, 3], (30, 40, 50, 60), 100]
 
l2
[[1, 2, 3], (30, 40)]

이 예에서 얕은 복사본의 원소는 원 대상 원소에 대한 인용이기 때문에 l2의 원소와 l1은 같은 목록과 원조 대상을 가리킨다.
l1[0].append(3), l1의 첫 번째 목록에 추가된 요소 3을 표시합니다.l2는 l1의 얕은 복사이기 때문에 l2의 첫 번째 요소와 l1의 첫 번째 요소는 같은 목록을 가리키기 때문에 l2의 첫 번째 목록도 이에 대응하는 새로운 요소 3을 추가할 수 있다.
l1[1]+=(50, 60)은 원조가 변할 수 없기 때문에 이것은 l1의 두 번째 원조에 대한 연결을 표시한 다음에 l1의 두 번째 요소로 새 원조를 다시 만들었고 l2에는 새 원조를 인용하지 않았기 때문에 l2는 영향을 받지 않았다.

깊이 있는 복사


깊이 복사란 메모리를 재분배하여 새로운 대상을 만들고 원래 대상의 요소를 귀속시키는 방식으로 새로운 하위 대상을 만들어서 새 대상으로 복사하는 것을 말한다.따라서 새 대상과 원 대상은 아무런 연관이 없다.
Python에서 copy.deepcopy () 를 사용하여 대상의 깊이 있는 복사를 실현합니다.
import copy
l1 = [[1, 2], (30, 40)]
l2 = copy.deepcopy(l1)
l1.append(100)
l1[0].append(3)
 
l1
[[1, 2, 3], (30, 40), 100]
 
l2 
[[1, 2], (30, 40)]

그러나 깊이 있는 복사도 완벽하지 않아 일련의 문제를 초래할 수 있다.복제된 객체에 자신을 가리키는 참조가 있으면 프로그램은 무한 루프에 빠지기 쉽습니다.
import copy
x = [1]
x.append(x)
 
x
[1, [...]]
 
y = copy.deepcopy(x)
y
[1, [...]]

여기에 Stack overflow가 나타나지 않는 이유는 깊이 복사 함수인 deepcopy에서 복사된 대상과 ID를 기록하는 사전이 유지되기 때문이다.복사 과정에서 사전에 복사할 대상이 저장되어 있으면 사전에서 바로 되돌아옵니다.
def deepcopy(x, memo=None, _nil=[]):
    """Deep copy operation on arbitrary Python objects.
    	
	See the module's __doc__ string for more info.
	"""
	
    if memo is None:
        memo = {}
    d = id(x) #   x   id
	y = memo.get(d, _nil) #  
	if y is not _nil:
	    return y #  , 
        ...    

Python 매개 변수 전달


Python에서 매개 변수의 전달은 값 전달이거나 대상의 인용 전달이라고 합니다.이곳의 값이나 대상의 인용 전달은 구체적인 메모리 주소를 가리키는 것이 아니라 구체적인 대상을 가리킨다.
4
  • 객체가 가변적이라면 객체가 변경될 때 해당 객체를 가리키는 모든 변수가 변경됩니다

  • 4
  • 만약에 대상이 변할 수 없다면 간단한 할당값은 그 중의 한 변수의 값만 바꿀 수 있고 나머지 변수는 영향을 받지 않는다

  • 예를 들면 다음과 같습니다.
    def my_func1(b):
    	b = 2
     
    a = 1
    my_func1(a)
    a
    1
    

    이곳의 매개 변수는 변수 a와 b가 동시에 1이라는 대상을 가리키도록 전달한다.그러나 우리가 b=2를 실행할 때 시스템은 값이 2인 새로운 대상을 다시 만들고 b가 그것을 가리키도록 한다.a는 여전히 1이라는 대상을 가리킨다.그래서 a의 값은 변하지 않고 여전히 1이다.
    def my_func3(l2):
    	l2.append(4)
     
    l1 = [1, 2, 3]
    my_func3(l1)
    l1
    [1, 2, 3, 4]
    

    여기 l1과 l2는 먼저 동시에 지향하는 값이 [1, 2, 3]인 목록이다.그러나 목록이 변할 수 있기 때문에 append() 함수를 실행하고 그 끝에 새로운 요소 4를 추가하면 변수 l1과 l2의 값도 바뀐다.
    def my_func4(l2):
    	l2 = l2 + [4]
     
    l1 = [1, 2, 3]
    my_func4(l1)
    l1
    [1, 2, 3]
    

    여기 l2=l2+[4]는'끝에 원소 4'를 넣은 새로운 목록을 만들고 l2가 이 새로운 대상을 가리키도록 합니다. 이 과정은 l1과 무관하기 때문에 l1의 값은 변하지 않습니다.

    장식기


    먼저 우리는 장식기의 간단한 예를 보았다.
    def my_decorator(func):
        def wrapper():
            print('wrapper of decorator')
            func()
        return wrapper
     
    def greet():
        print('hello world')
     
    greet = my_decorator(greet)
    greet()
     
    #  
    wrapper of decorator
    hello world
    

    이 코드에서 변수 greet은 내부 함수 wrapper () 를 가리키고 내부 함수 wrapper () 는 원 함수 greet () 를 호출하기 때문에 마지막으로 greet () 를 호출할 때 'wrapper of decorator' 를 출력하고 'Hello World' 를 출력합니다.
    my_decorator () 는 진정으로 실행해야 할 함수greet () 를 그 안에 감싸고 행동을 바꾸는 장식기입니다.
    python에서 보다 우아한 방법을 사용할 수 있습니다.
    def my_decorator(func):
        def wrapper():
            print('wrapper of decorator')
            func()
        return wrapper
     
    @my_decorator
    def greet():
        print('hello world')
     
    greet()
    

    @my_decorator는 앞의greet=my 에 해당한다decorator 문
    일반적인 상황에서, 우리는args와**kwargs를 장식기 내부 함수 wrapper ()의 매개 변수로 사용할 것이다.args와 **kwargs는 임의의 수량과 유형의 매개 변수를 받아들이는 것을 의미하기 때문에 장식기는 다음과 같은 형식으로 쓸 수 있습니다.
    def my_decorator(func):
        def wrapper(*args, **kwargs):
            print('wrapper of decorator')
            func(*args, **kwargs)
        return wrapper
    

    이렇게 하면 장식기가 임의의 매개 변수를 받아들일 수 있다.

    사용자 정의 매개 변수의 장식기


    예를 들어 장식기 내부 함수가 실행되는 횟수를 표시하기 위해 파라미터를 정의하고 싶습니다
    def repeat(num):
        def my_decorator(func):
            def wrapper(*args, **kwargs):
                for i in range(num):
                    print('wrapper of decorator')
                    func(*args, **kwargs)
            return wrapper
        return my_decorator
     
     
    @repeat(4)
    def greet(message):
        print(message)
     
    greet('hello world')
     
    #  :
    wrapper of decorator
    hello world
    wrapper of decorator
    hello world
    wrapper of decorator
    hello world
    wrapper of decorator
    hello world
    

    원 함수의 메타 정보 보존


    다음과 같습니다.
    greet.__name__
    ##  
    'wrapper'
     
    help(greet)
    #  
    Help on function wrapper in module __main__:
     
    wrapper(*args, **kwargs)
    

    greet () 함수가 장식된 후 원 정보가 바뀌었습니다.원 정보는 "그것은 더 이상 이전의greet () 함수가 아니라 wrapper () 함수로 대체되었습니다."라고 알려 줍니다.
    따라서 내장된 장식기 @functools를 추가할 수 있습니다.랩, 원 함수의 원 정보를 보존하는 데 도움을 줍니다.다음과 같습니다.
    import functools
     
    def my_decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            print('wrapper of decorator')
            func(*args, **kwargs)
        return wrapper
        
    @my_decorator
    def greet(message):
        print(message)
     
    greet.__name__
     
    #  
    'greet'
    

    클래스 장식기


    클래스 장식기는 주로 함수에 의존call_(), 클래스의 예시를 호출할 때마다 함수call__()는 한 번 실행됩니다.
    class Count:
        def __init__(self, func):
            self.func = func
            self.num_calls = 0
     
        def __call__(self, *args, **kwargs):
            self.num_calls += 1
            print('num of calls is: {}'.format(self.num_calls))
            return self.func(*args, **kwargs)
     
    @Count
    def example():
        print("hello world")
     
    example()
     
    #  
    num of calls is: 1
    hello world
     
    example()
     
    #  
    num of calls is: 2
    hello world
      
    

    장식기의 중첩


    예:
    @decorator1
    @decorator2
    @decorator3
    def func():
        ...
    

    다음과 같습니다.
    decorator1(decorator2(decorator3(func)))
    

    예:
    import functools
     
    def my_decorator1(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            print('execute decorator1')
            func(*args, **kwargs)
        return wrapper
     
     
    def my_decorator2(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            print('execute decorator2')
            func(*args, **kwargs)
        return wrapper
     
     
    @my_decorator1
    @my_decorator2
    def greet(message):
        print(message)
     
     
    greet('hello world')
     
    #  
    execute decorator1
    execute decorator2
    hello world
    

    협정하다


    협정과 다선정의 차이는 주로 두 가지에 있다. 하나는 협정이 단선정이다.둘째, 협정은 사용자가 어느 곳에서 통제권을 주고 다음 임무로 전환할지 결정한다.
    먼저 예를 살펴보겠습니다.
    import asyncio
     
    async def crawl_page(url):
        print('crawling {}'.format(url))
        sleep_time = int(url.split('_')[-1])
        await asyncio.sleep(sleep_time)
        print('OK {}'.format(url))
     
    async def main(urls):
        tasks = [asyncio.create_task(crawl_page(url)) for url in urls]
        for task in tasks:
            await task
     
    %time asyncio.run(main(['url_1', 'url_2', 'url_3', 'url_4']))
     
    ##########   ##########
     
    crawling url_1
    crawling url_2
    crawling url_3
    crawling url_4
    OK url_1
    OK url_2
    OK url_3
    OK url_4
    Wall time: 3.99 s
    

    협업을 수행하는 데는 여러 가지 방법이 있는데, 여기서 나는 자주 사용하는 세 가지를 소개한다.
    우선, 우리는 await를 통해 호출할 수 있다.await가 실행하는 효과는 Python이 정상적으로 실행하는 것과 같다. 즉, 프로그램이 여기에 막혀 호출된 협동 함수에 들어가 실행이 끝난 후에 다시 계속된다는 것이다. 이것은 await의 글자 그대로이다.
    그 다음으로 우리는 asyncio를 통과할 수 있다.create_task () 를 사용하여 작업을 만듭니다.모든 작업이 끝나면 for task in tasks: await task를 사용하면 됩니다.
    마지막으로, 우리는 asyncio가 필요하다.run에서 실행을 터치합니다.asyncio.run 이 함수는 Python 3.7 이후에만 있는 특성입니다.아주 좋은 프로그래밍 규범은 asyncio입니다.run (main ()) 은 주 프로그램의 입구 함수로 프로그램 실행 주기 내에 asyncio를 한 번만 호출합니다.run.
    위의 예에서도 await asyncio를 사용할 수 있습니다.gather(*tasks) - 모든 작업을 기다립니다.
    import asyncio
     
    async def crawl_page(url):
        print('crawling {}'.format(url))
        sleep_time = int(url.split('_')[-1])
        await asyncio.sleep(sleep_time)
        print('OK {}'.format(url))
     
    async def main(urls):
        tasks = [asyncio.create_task(crawl_page(url)) for url in urls]
        await asyncio.gather(*tasks)
     
    %time asyncio.run(main(['url_1', 'url_2', 'url_3', 'url_4']))
     
    ##########   ##########
     
    crawling url_1
    crawling url_2
    crawling url_3
    crawling url_4
    OK url_1
    OK url_2
    OK url_3
    OK url_4
    Wall time: 4.01 s
    

    협동 중단 및 이상 처리

    import asyncio
     
    async def worker_1():
        await asyncio.sleep(1)
        return 1
     
    async def worker_2():
        await asyncio.sleep(2)
        return 2 / 0
     
    async def worker_3():
        await asyncio.sleep(3)
        return 3
     
    async def main():
        task_1 = asyncio.create_task(worker_1())
        task_2 = asyncio.create_task(worker_2())
        task_3 = asyncio.create_task(worker_3())
     
        await asyncio.sleep(2)
        task_3.cancel()
     
        res = await asyncio.gather(task_1, task_2, task_3, return_exceptions=True)
        print(res)
     
    %time asyncio.run(main())
     
    ##########   ##########
     
    [1, ZeroDivisionError('division by zero'), CancelledError()]
    Wall time: 2 s
    

    이 예에서task 를 사용했다3. cancel () 로 코드를 중단하고 return 을 사용합니다exceptions=True로 출력 이상을 제어합니다. 설정하지 않으면 오류가 완전히throw로 실행되어 try except가 포착해야 합니다. 이것은 실행되지 않은 다른 작업이 모두 취소된다는 것을 의미합니다.

    Python의 쓰레기 회수 메커니즘


    python은 인용 계수 메커니즘을 위주로 하고 표기-제거와 세대별 수집(세대별 회수) 두 가지 메커니즘을 보조로 하는 전략을 채택한다.

    인용 계수법


    인용 계수법 메커니즘의 원리는 각 대상이 하나의ob 를 유지하는 것이다ref 필드는 이 대상이 현재 인용된 횟수를 기록하는 데 사용되며, 새로운 인용이 이 대상을 가리킬 때마다 인용 계수obref 더하기 1, 이 대상의 인용이 효력을 잃을 때마다 계수 obref 마이너스 1, 대상의 인용 계수가 0이 되면 이 대상은 즉시 회수되고 대상이 차지하는 메모리 공간은 방출됩니다.
    그것의 단점은 대상의 순환 인용을 해결할 수 없다는 것이다.

    태그 지우기 알고리즘


    하나의 지향도에 대해 만약에 하나의 노드에서 출발하여 두루 훑어보고 지나간 모든 노드를 표시한다.그러면 반복이 끝난 후에 표기되지 않은 모든 노드를 우리는 도달할 수 없는 노드라고 부른다.이 노드들의 존재는 아무런 의미가 없고 자연히 쓰레기 회수가 필요하다는 것을 알 수 있다.
    Python의 쓰레기 회수 실현에서mark-sweep은 양방향 체인 테이블을 사용하여 데이터 구조를 유지하고 용기류의 대상만 고려한다(용기류의 대상만 순환 인용이 발생할 수 있다).

    세대별 수집 알고리즘


    파이톤은 모든 대상을 3세대로 나눈다.방금 창립된 대상은 0세대이다.한 차례의 쓰레기 수거를 거친 후에도 여전히 존재하는 대상은 순서대로 전 세대에서 다음 세대로 옮겨진다.모든 세대가 자동 쓰레기 회수를 시작하는 한도값은 따로 지정할 수 있다.스팸 수거기에서 새로운 대상이 삭제 대상을 빼고 해당하는 한도값에 도달하면 이 세대 대상에 대해 스팸 수거를 시작한다.

    좋은 웹페이지 즐겨찾기