2021年7月31日 星期六

[ Python 常見問題 ] Python type-hinting, indexable object

 Source From Here

Question
My function needs to accept an object, from which data can be extracted by index, viz. a List or an instance with defined __getitem__ method.

Which type can I use for type hinting this argument?

HowTo
There are several different ways you can do this.

If you're ok with using only custom classes (that you can write) as indexable containers:
  1. from typing import TypeVar, Generic  
  2.   
  3. ReturnType = TypeVar('ReturnType')  
  4.   
  5. class IndexableContainer(Generic[ReturnType]):  
  6.   def __getitem__(self, key: int) -> ReturnType:  
  7.     ...  
  8.   
  9.   
  10. def get_last_element(container: IndexableContainer[int]) -> int:  
  11.   return container[-1]  
  12.   
  13.   
  14. my_container = [1,2,3]  
  15. print(get_last_element(my_container))  
Output:
3

The issue is, of course, that if you wanted to pass in a plain old list into the function, you wouldn't be able to do so since list doesn't subclass your custom type.

We could maybe special-case certain inputs via clever use of the @overload decorator and unions, but there's a second, albeit experimental, way of doing this known as Protocols.

Protocols basically let you express "duck typing" in a sane way using type hints: the basic idea is that we can tweak IndexableContainer to become a protocol. Now, any object that implements the __getitem__ method with the appropriate signature is counted as a valid IndexableContainer, whether or not they subclass that type or not.

The only caveat is that Protocols are currently experimental and (afaik) only supported by mypy. The plan is to eventually add protocols to the general Python ecosystem -- see PEP 544 for the specific proposal -- but I haven't kept track of the discussion/don't know what the status of that is.

In any case, to use protocols, install the typing_extensions module using pip. Then, you can do the following:
  1. from typing import TypeVar, Generic  
  2. from typing_extensions import Protocol  
  3.   
  4. ReturnType = TypeVar('ReturnType')  
  5.   
  6. class IndexableContainer(Protocol, Generic[ReturnType]):  
  7.   def __getitem__(self, key: int) -> ReturnType:  
  8.     ...  
  9.   
  10.   
  11. def get_last_element(container: IndexableContainer[int]) -> int:  
  12.   return container[-1]  
  13.   
  14.   
  15. my_container = [1,2,3]  
  16. print(get_last_element(my_container))  
The last ane easier one is to use typing Sequence:
  1. from typing import TypeVar, Sequence  
  2.   
  3. EType = TypeVar('ElementType'int, str)  
  4.   
  5.   
  6. def get_last_element(container: Sequence[EType]) -> EType:  
  7.   return container[-1]  
  8.   
  9.   
  10. my_int_container = [1,2,3]  
  11. my_str_container = ["a""b""c"]  
  12. print(get_last_element(my_int_container))  
  13. print(get_last_element(my_str_container))  
Ouptut
3
c


2021年7月25日 星期日

[ Bash 範例代碼 ] List logged user by date

 Source From Here

09-1_list_of_logged_users.sh
  1. #!/bin/bash  
  2. # Author: John Lee  
  3. # Date: 2021/07/25  
  4. # Description: This script will list users logged in by date  
  5. # Info:  
  6. #   $ last | head -n 3  
  7. #   root     pts/1        192.168.37.1     Sun Jul 25 00:16    gone - no logout  
  8. #   root     pts/1        192.168.37.1     Sat Jul 24 18:41 - 20:33  (01:51)  
  9. #   root     pts/1        192.168.37.1     Fri Jul 23 23:00 - 05:13  (06:13)  
  10.   
  11. # e.g.: Sun Jul 25  
  12. today=`date | awk '{print $1,$2,$3}'`  
  13.   
  14. echo -n "Give month to search logged user(e.g: `date +'%b'`): "  
  15. read month  
  16. echo -n "Give day to search logged user(e.g.: `date +'%d'`): "  
  17. read day  
  18.   
  19. if [ ! -z "$month" ] && [ ! -z "$day" ]; then  
  20.   tdate="$month\s+$day\s+"  
  21. else  
  22.   tdate=$today  
  23.   month=`date +'%b'`  
  24.   day=`date +'%d'`  
  25. fi  
  26.   
  27. echo "Search logged user on date='$month $day'"  
  28. for u in `last | grep -P "${tdate}" | awk '{print $1}' | sort | uniq`  
  29. do  
  30.   echo "Found user '$u'"  
  31. done  
Execution output:
# last | grep -P 'Mon May\s+3\s+'
root pts/1 192.168.37.1 Mon May 3 23:08 - 01:32 (02:24)
root pts/0 192.168.37.1 Mon May 3 22:18 - 03:49 (05:30)
john :0 :0 Mon May 3 22:18 - crash (13+01:54)
...


# ./09-1_list_of_logged_users.sh
Give month to search logged user(e.g: Jul): May
Give day to search logged user(e.g.: 25): 3
Search logged user on date='May 3'
Found user 'john'
Found user 'reboot'
Found user 'root'
Found user 'wtmp'


2021年7月24日 星期六

[Py Tricks] 2.3 Context Managers and the with Statement

Source From Here
Preface
The with statement in Python is regarded as an obscure feature by some. But when you peek behind the scenes, you’ll see that there’s no magic involved, and it’s actually a highly useful feature that can help you write cleaner and more readable Python code.

So what’s the with statement good for? It helps simplify some common resource management patterns by abstracting their functionality and allowing them to be factored out and reused. A good way to see this feature used effectively is by looking at examples in the Python standard library. The built-in open() function provides us with an excellent use case:
  1. with open('hello.txt''w') as f:  
  2.   f.write('hello, world!')  
Opening files using the with statement is generally recommended because it ensures that open file descriptors are closed automatically after program execution leaves the context of the with statement. Internally, the above code sample translates to something like this:
  1. f = open('hello.txt''w')  
  2. try:  
  3.   f.write('hello, world')  
  4. finally:  
  5.   f.close()  
You can already tell that this is quite a bit more verbose. Note that the try...finally statement is significant. It wouldn’t be enough to just write something like this:
  1. f = open('hello.txt''w')  
  2. f.write('hello, world')  
  3. f.close()  
This implementation won’t guarantee the file is closed if there’s an exception during the f.write() call—and therefore our program might leak a file descriptor. That’s why the with statement is so useful. It makes properly acquiring and releasing resources a breeze.

Another good example where the with statement is used effectively in the Python standard library is the threading.Lock class:
  1. some_lock = threading.Lock()  
  2. # Harmful:  
  3. some_lock.acquire()  
  4. try:  
  5.   # Do something...  
  6. finally:  
  7.   some_lock.release()  
  8.   
  9. # Better:  
  10. with some_lock:  
  11.   # Do something...  
In both cases, using a with statement allows you to abstract away most of the resource handling logic. Instead of having to write an explicit try...finally statement each time, using the with statement takes care of that for us.

The with statement can make code that deals with system resources more readable. It also helps you avoid bugs or leaks by making it practically impossible to forget to clean up or release a resource when it’s no longer needed.

Supporting with in Your Own Objects
Now, there’s nothing special or magical about the open() function or the threading.Lock class and the fact that they can be used with a with statement. You can provide the same functionality in your own classes and functions by implementing so-called context managers.

What’s a context manager? It’s a simple “protocol” (or interface) that your object needs to follow in order to support the with statement. Basically, all you need to do is add __enter__ and __exit__ methods to an object if you want it to function as a context manager. Python will call these two methods at the appropriate times in the resource management cycle.

Let’s take a look at what this would look like in practical terms. Here’s what a simple implementation of the open() context manager might look like:
  1. class ManagedFile:  
  2.   def __init__(self, name):  
  3.     self.name = name  
  4.   
  5.   def __enter__(self):  
  6.     self.file = open(self.name, 'w')  
  7.     return self.file  
  8.   
  9.   def __exit__(self, exc_type, exc_val, exc_tb):  
  10.     if self.file:  
  11.       self.file.close()  
Our ManagedFile class follows the context manager protocol and now supports the with statement, just like the original open() example did:
  1. with ManagedFile('hello.txt') as f:  
  2.   f.write('hello, world!')  
  3.   f.write('bye now')  
Python calls __enter__ when execution enters the context of the with statement and it’s time to acquire the resource. When execution leaves the context again, Python calls __exit__ to free up the resource.

Writing a class-based context manager isn’t the only way to support the with statement in Python. The contextlib utility module in the standard library provides a few more abstractions built on top of the basic context manager protocol. This can make your life a little easier if your use cases match what’s offered by contextlib.

For example, you can use the contextlib.contextmanager decorator to define a generator-based factory function for a resource that will then automatically support the with statement. Here’s what rewriting our ManagedFile context manager example with this technique looks like:
from contextlib import contextmanager
  1. @contextmanager  
  2. def managed_file(name):  
  3. try:  
  4.   f = open(name, 'w')  
  5.   yield f  
  6. finally:  
  7.   f.close()  
Example usage:
  1. with managed_file('hello.txt') as f:  
  2.   f.write('hello, world!')  
  3.   f.write('bye now')  
In this case, managed_file() is a generator that first acquires the resource. After that, it temporarily suspends its own execution and yields the resource so it can be used by the caller. When the caller leaves the with context, the generator continues to execute so that any remaining clean-up steps can occur and the resource can get released back to the system.

The class-based implementation and the generator-based one are essentially equivalent. You might prefer one over the other, depending on which approach you find more readable.

A downside of the @contextmanager-based implementation might be that it requires some understanding of advanced Python concepts like decorators and generators. If you need to get up to speed with those, feel free to take a detour to the relevant chapters here in this book.

Once again, making the right implementation choice here comes down to what you and your team are comfortable using and what you find the most readable.

Writing Pretty APIs With Context Managers
Context managers are quite flexible, and if you use the with statement creatively, you can define convenient APIs for your modules and classes.

For example, what if the “resource” we wanted to manage was text indentation levels in some kind of report generator program? What if we could write code like this to do it:
  1. with Indenter() as indent:  
  2.   indent.print('hi!')  
  3.   with indent:  
  4.     indent.print('hello')  
  5.     with indent:  
  6.       indent.print('bonjour')  
  7.   indent.print('hey')  
This almost reads like a domain-specific language (DSL) for indenting text. Also, notice how this code enters and leaves the same context manager multiple times to change indentation levels. Running this code snippet should lead to the following output and print neatly formatted text to the console:
  1. hi!  
  2.     hello  
  3.         bonjour  
  4. hey  
So, how would you implement a context manager to support this functionality?

By the way, this could be a great exercise for you to understand exactly how context managers work. So before you check out my implementation below, you might want to take some time and try to implement this yourself as a learning exercise.

If you’re ready to check out my implementation, here’s how you might implement this functionality using a class-based context manager:
  1. class Indenter:  
  2.   def __init__(self):  
  3.     self.level = 0  
  4.   def __enter__(self):  
  5.     self.level += 1  
  6.     return self  
  7.   def __exit__(self, exc_type, exc_val, exc_tb):  
  8.     self.level -= 1  
  9.   
  10.   def print(self, text):  
  11.     print(' ' * self.level + text)  
That wasn’t so bad, was it? I hope that by now you’re already feeling more comfortable using context managers and the with statement in your own Python programs. They’re an excellent feature that will allow you to deal with resource management in a much more Pythonic and maintainable way.

Key Takeaways
• The with statement simplifies exception handling by encapsulating standard uses of try/finally statements in so-called context managers.
• Most commonly it is used to manage the safe acquisition and release of system resources. Resources are acquired by the with statement and released automatically when execution leaves the with context.
• Using with effectively can help you avoid resource leaks and make your code easier to read.

[ Python 常見問題 ] When using unittest.mock.patch, why is autospec not True by default?

  Source From  Here Question When you patch a function using  mock , you have the option to specify  autospec  as True: If you set  autospec...