zoukankan      html  css  js  c++  java
  • 读书笔记, Python

    To be a Pythonista

    1. assert

    syntax: assert expression1 [",", expression2]
    大致相当于
    if __debug__:
        if not expression1:
            raise AssertionError(expression2)
    

    例子1

    def apply_discount(product, discount):
        price = int(product['price'] * (1.0 - discount))
        assert 0 <= price <= product['price']
        return price
    

    例子2

    if cond == 'x':
        do_x()
    elif cond == 'y':
        do_y()
    else:
        assert False, (
            'This should never happen, 如果出现Heisenbug, 可以用这样的方式去debug
        )
    

    注意事项1: 可以用python -O .py去关闭assert, 所以assert只是辅助debugging, 而不能发现run-time errors

    # 如果关闭assert, 很危险
    def delete_product(prod_id, user):
        assert user.is_admin(), 'Must be admin'
        assert store.has_product(prod_id), 'Unknown product'
        store.get_product(prod_id).delete()
    

    应该改为

    def delete_product(product_id, user):
        if not user.is_admin():
            raise AuthError('Must be admin to delete')
        if not store.has_product(product_id):
            raise ValueError('Unknown product id')   
        store.get_product(product_id).delete()
    

    注意事项2: assert后面跟元祖, 永远为true. 不过python3会有提示

    def hello_assert():
        assert (1==2, 'This should fail')
    
    # SyntaxWarning: assertion is always true, perhaps remove parentheses?
    

    2. String literal concatenation

    这样就可以不用续行符了

    my_str = ('This is a super long string constant '
        'spread out across multiple lines. '
        'And look, no backslash characters needed!')
    

    双刃剑. Python’s string literal concatenation feature can work to your benefit, or introduce hard-to-catch bugs.

    另外, 最好在容器最后的元素加上逗号

    names = [
        'Alice',
        'Bob',
        'Dilbert',
    ]
    

    3. Context manager. 上下文管理器

    有基于Class(实现__enter__, __exit__)或者contextlib这个库和生成器

    例子1. 基于Class

    Class ManagedFile:
        def __init__(self, name):
            self.name = name
        
        def __enter__(self):
            self.file = open(self.name, 'w')
            return self.file
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            if self.file:
                self.file.close()
    
        with ManagedFile('hello.txt') as f:
            f.write('hello, world
    ')
            f.write('bye now
    ')
    

    例子2. 基于contextlib.contextmananger和generator-based factory function

    from contextlib import contextmanager
    
    @contextmanager
    def managed_file(name):
        try:
            f = open(name, 'w')
            yield f
        finally:
            f.close()
    
    with managed_file('hello.txt') as f:
        f.write('hello, world
    ')
        f.write('bye now
    ')
    

    例子3

    what if the “resource” we wanted to manage was text indentation levels in some kind of report generator program

    class Indenter:
        def __init__(self):
            self.level = -1
    
        def __enter__(self):
            self.level += 1
            return self
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            self.level -= 1
        
        def print(self, text):
            print('  ' * self.level + text)
    
    with Indenter() as indent:
        indent.print('h1!')
        with indent:
            indent.print('hello')
            with indent:
                indent.print('bonjour')
        indent.print('hey')
    

    练习1. 计算程序需要耗费的时间(还可以基于修饰器)

    个人理解如果Timer基于context manager的话,可以计算一个context里多个函数的耗费时间, 而修饰器只能计算被修饰函数的耗费时间.

    # 基于Class
    import time
    
    class Timer:
        def __init__(self):
            self.start_time = 0
    
        def __enter__(self):
            self.start_time = time.time()
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            end_time = time.time()
            print(end_time - self.start_time)
    
    with Timer() as timer:
        print('******' * 30)
    
    # 基于contextlib.contextmanager和生成器
    import time
    from contextlib import contextmanager
    
    
    @contextmanager
    def timer():
        try:
            start_time = time.time()
            yield 
        finally:
            end_time = time.time()
            print(end_time - start_time)
    
    def do_something():
        time.sleep(2)
    
    with timer():
        do_something()
    

    4. Underscores, Dunders, and More

    看PEP8

    • Single Leading Underscore: var
    • Single Trailing Underscore: var

    • Double Leading Underscore: __var
    • Double Leading and Trailing Underscore: __var__
    • Single Underscore: _

    name mangling中要注意的点

    _MangledGlobal__mangled = 23
    
    class MangledGlobal:
        def test(self):
            return __mangled
    
    MangledGlobal().test() # 23
    

    5. A Shocking Truth About String Formatting

    假设有两个变量

    >>> errno = 50159747054
    >>> name = 'Bob'
    

    字符串格式化1, old style, 使用%

    “old style” formatting has been de-emphasized, it hasn’t been deprecated.

    'Hello %s' % name
    
    # 还可以加上format specifiers去控制输出的字符串
    '%x' % errno # 输出16进制
    
    # 注意"#"只接收一个参数,所以多参数要包在一个tuple里
    >>> 'Hey %s, there is a 0x%x error!' % (name, errno)
    'Hey Bob, there is a 0xbadc0ffee error!
    

    支持mapping

    >>> 'Hey %(name)s, there is a 0x%(errno)x error!' % {
    ... "name": name, "errno": errno }
    'Hey Bob, there is a 0xbadc0ffee error!'
    

    字符串格式化2, new style, format()

    >>> 'Hello, {}'.format(name)
    'Hello, Bob'
    
    >>> 'Hey {name}, there is a 0x{errno:x} error!'.format(
    ... name=name, errno=errno)
    'Hey Bob, there is a 0xbadc0ffee error!'
    

    starting with Python 3.6 there’s an even better way to format your strings

    字符串格式化3, f''

    Python 3.6 adds yet another way to format strings, called Formatted String Literals

    >>> f'Hello, {name}!'
    'Hello, Bob!'
    
    >>> a = 5
    >>> b = 10
    >>> f'Five plus ten is {a + b} and not {2 * (a + b)}.'
    'Five plus ten is 15 and not 30.'
    
    >>> def greet(name, question):
    ...         return f"Hello, {name}! How's it {question}?"
    ...
    >>> greet('Bob', 'going')
    "Hello, Bob! How's it going?"
    
    # 差不多相等
    >>> def greet(name, question):
    ...         return ("Hello, " + name + "! How's it " +
                        question + "?")
    
    The real implementation is slightly faster than that because it uses the
    BUILD_STRING opcode as an optimization.14 But functionally they’re
    the same:
    >>> import dis
    >>> dis.dis(greet)
    2 0 LOAD_CONST 1 ('Hello, ')
    2 LOAD_FAST 0 (name)
    4 FORMAT_VALUE 0
    6 LOAD_CONST 2 ("! How's it ")
    8 LOAD_FAST 1 (question)
    10 FORMAT_VALUE 0
    12 LOAD_CONST 3 ('?')
    14 BUILD_STRING 5
    16 RETURN_VALUE
    
    # f''同样支持format specifiers
    >>> f"Hey {name}, there's a {errno:#x} error!"
    "Hey Bob, there's a 0xbadc0ffee error!"
    
    # Python’s new Formatted String Literals are similar to the JavaScript Template Literals added in ES2015.
    

    字符串格式化4, Template Strings

    It’s a simpler and less powerful mechanism.

    >>> from string import Template
    >>> t = Template('Hey, $name!')
    >>> t.substitute(name=name)
    'Hey, Bob!'
    
    # Another difference is that template strings don’t allow format specifiers.
    >>> templ_string = 'Hey $name, there is a $error error!'
    >>> Template(templ_string).substitute(
    ... name=name, error=hex(errno))
    'Hey Bob, there is a 0xbadc0ffee error!'
    
    # In my opinion, the best use case for template strings is when you’re handling format strings generated by users of your program. Due to their reduced complexity, template strings are a safer choice
    
    # 有安全隐患的
    >>> SECRET = 'this-is-a-secret'
    >>> class Error:
                def __init__(self):
                    pass
    >>> err = Error()
    >>> user_input = '{error.__init__.__globals__[SECRET]}'
    # Uh-oh...
    >>> user_input.format(error=err)
    'this-is-a-secret'
    
    # Template String is much safer
    >>> user_input = '${error.__init__.__globals__[SECRET]}'
    >>> Template(user_input).substitute(error=err)
    ValueError:
    "Invalid placeholder in string: line 1, col 1"
    

    Dan’s Python String Formatting Rule of Thumb:

    If your format strings are user-supplied, use Template
    Strings to avoid security issues. Otherwise, use Literal
    String Interpolation if you’re on Python 3.6+, and “New
    Style” String Formatting if you’re not.
    

    6. “The Zen of Python” Easter Egg

    Tim Peters’ Zen of Python

    import this
    

    Chapter 3 Effective Functions

    7. Python’s Functions Are First-Class Citizens. 函数是一等公民

    Python’s functions are first-class objects. You can assign them to variables, store them in data structures, pass them as arguments to other functions, and even return them as values from other functions.

    假设

    def yell(text):
        return text.upper() + '!'
    
    >>> yell('hello")
    'HELLO!'
    

    Functions are objects

    # It takes the function object referenced by yell and creates a second name, bark, that points to it
    bark = yell
    
    >>> bark('woof')
    'WOOF!'
    
    # Function objects and their names are two separate concerns.(A variable pointing to a function and the function itself are really two separate concerns)
    >>> del yell
    >>> yell('hello?')
    NameError: "name 'yell' is not defined"
    >>> bark('hey')
    'HEY!'
    
    # By the way, Python attaches a string identifier to every function at creation time for debugging purposes. You can access this internal
    # identifier with the __name__ attribute
    >>> bark.__name__
    'yell'
    

    Functions Can Be Stored in Data Structures. 函数可以存储在数据结构中

    >>> funcs = [bark, str.lower, str.capitalize]
    >>> funcs
    [<function yell at 0x10ff96510>,
    <method 'lower' of 'str' objects>,
    <method 'capitalize' of 'str' objects>]
    
    >>> for f in funcs:
    ...        print(f, f('hey there'))
    <function yell at 0x10ff96510> 'HEY THERE!'
    <method 'lower' of 'str' objects> 'hey there'
    <method 'capitalize' of 'str' objects> 'Hey there'
    
    >>> funcs[0]('heyho')
    'HEYHO!'
    

    Functions Can Be Passed to Other Functions. 函数可以传递给另外一个函数

    Functions that can accept other functions as arguments are also called higher-order functions. They are a necessity for the functional programming style.

    def greet(func):
        greeting = func('Hi, I am a Python program')
        print(greeting)
    
    >>> greet(bark)
    'HI, I AM A PYTHON PROGRAM!'
    
    def whisper(text):
        return text.lower() + '...'
    >>> greet(whisper)
    'hi, i am a python program...'
    
    # 典型的high order函数:map
    >>> list(map(bark, ['hello', 'hey', 'hi']))
    ['HELLO!', 'HEY!', 'HI!']
    

    Functions Can Be Nested 函数可以定义在另外一个函数里

    def speak(text):
        def whisper(t):
            return t.lower() + '...'
        return whisper(text)
    >>> speak('Hello, World')
    'hello, world...'
    
    # 注意。Here’s the kicker though—whisper does not exist outside speak:
    >>> whisper('Yo')
    NameError:
    "name 'whisper' is not defined"
    >>> speak.whisper
    AttributeError:
    "'function' object has no attribute 'whisper'"
    
    # 如果想访问内部函数怎么办?因为函数是一个对象,返回它就可以。
    def get_speak_func(volume):
    
        def whisper(text):
            return text.lower() + '...'
        
        def yell(text):
            return rext.upper() + '!'
    
        if volume > 0.5:
            return yell
        else:
            return whisper
    
    print(get_speak_func(0.3))
    print(get_speak_func(0.7))
    

    Functions Can Capture Local State

    Not only can functions return other functions, these inner functions can also capture and carry some of the parent function’s state with them. 闭包, lexical closures (or just closures, for short)

    闭包解释

    A closure remembers the values from its enclosing lexical scope even when the program flow is no longer in that scope.
    

    闭包意义

    In practical terms, this means not only can functions return behaviors but they can also pre-configure those behaviors
    

    例子

    def make_adder(n):
        def add(x):
            return x + n
        return add
    
    
    plus_3 = make_adder(3)
    plus_5 = make_adder(5)
    
    print(plus_3(4))
    print(plus_5(4))
    
    # In this example, make_adder serves as a factory to create and configure “adder” functions. Notice how the “adder” functions can still access the n argument of the make_adder function (the enclosing scope)
    

    Objects Can Behave Like Functions

    This is all powered by the call dunder method

    class Adder:
        def __init__(self, n):
            self.n = n
    
        def __call__(self, x):
            return self.n + x
    
    >>> plus_3 = Adder(3)
    >>> plus_3(4)
    

    8. Lambdas Are Single-Expression Functions

    隐含return, 不用name绑定就可以用

    add = lambda x, y: x + y
    add(5, 3)
    
    (lambda x, y: x + y)(5, 3)
    

    Lambdas You Can Use, 排序的时候

    >>> tuples = [(1, 'd'), (2, 'b'), (4, 'a'), (3, 'c')]
    >>> sorted(tuples, key=lambda x: x[1])
    [(4, 'a'), (2, 'b'), (3, 'c'), (1, 'd')]
    
    >>> sorted(range(-5, 6), key=lambda x: x * x)
    [0, -1, 1, -2, 2, -3, 3, -4, 4, -5, 5]
    

    Just like regular nested functions, lambdas also work as lexical closures. Lambda可以是闭包,也即是一个沙盒。

    def make_adder(n):
        return lambda x: x + n
    
    plus_3 = make_adder(3)
    plus_5 = make_adder(5)
    
    print(plus_3(4))
    print(plus_5(4))
    

    应该少用lambda

    因为可读性和维护性差。Always ask yourself: Would using a regular (named) function or a list comprehension offer more clarity?

    # Harmful:
    >>> class Car:
    ...         rev = lambda self: print('Wroom!')
    ...         crash = lambda self: print('Boom!')
    >>> my_car = Car()
    >>> my_car.crash()
    'Boom!'
    
    # Harmful:
    >>> list(filter(lambda x: x % 2 == 0, range(16)))
    [0, 2, 4, 6, 8, 10, 12, 14]
    # Better:
    >>> [x for x in range(16) if x % 2 == 0]
    [0, 2, 4, 6, 8, 10, 12, 14]
    

    9. The Power of Decorators

    可以用在以下情景

    logging 日志
    enforcing access control and authentication 访问与授权控制
    计算耗费的时间
    限流
    缓存等等
    

    基本

    def uppercase(func):
        def wrapper():
            original_result = func()
            modified_result = original_result.upper()
            return modified_result
        return wrapper
    
    @uppercase
    def greet():
        return 'Hello!'
    >>> greet()
    'HELLO!'
    

    多个装饰器

    def strong(func):
        def wrapper():
            return '<strong>' + func() + '</strong>'
        return wrapper
    
    def emphasis(func):
        def wrapper():
            return '<em>' + func() + '</em>'
        return wrapper
    
    @strong
    @emphasis
    def greet():
        return 'Hello!'
    
    >>> greet()
    '<strong><em>Hello!</em></strong>'
    
    # 相当于 decorated_greet = strong(emphasis(greet))
    

    修饰带参数的函数

    def trace(func):
        def wrapper(*args, **kwargs):
            print(f'TRACE: calling {func.__name__}() '
                f'with {args}, {kwargs}')
            original_result = func(*args, **kwargs)
            print(f'TRACE: {func.__name__}() '
                    f'returned {original_result!r}')
            return original_result
        return wrapper
    
    @trace
    def say(name, line):
        return f'{name}: {line}'
    
    print(say('Jane', 'Hello World'))
    

    保持被修饰函数的metadata

    import functools
    
    def uppercase(func):
        @functools.wraps(func)
        def wrapper():
            return func().upper()
        return wrapper
    
    
    @uppercase
    def greet():
        """Return a friendly greeting"""
        return 'hello!'
    
    print(greet.__name__)
    print(greet.__doc__)
    print(greet())
    


    10. 关于*args, **kwargs

    They allow a
    function to accept optional arguments, so you can create flexible APIs in your modules and classes

    def foo(required, *args, **kwargs):
        print(required)
        if args:
            print(args)
        if kwargs:
            print(kwargs)
    
    >>> foo()
    TypeError:
    "foo() missing 1 required positional arg: 'required'"
    >>> foo('hello')
    hello
    >>> foo('hello', 1, 2, 3)
    hello
    (1, 2, 3)
    >>> foo('hello', 1, 2, 3, key1='value', key2=999)
    hello
    (1, 2, 3)
    {'key1': 'value', 'key2': 999}
    

    Forwarding Optional or Keyword Arguments(Function Argument Unpacking)

    用*或**去unpack参数,传递给另外一个函数

    def foo(x, *args, **kwargs):
        kwargs['name'] = 'Alice'
        new_args = args + ('extra', )
        bar(x, *new_args, **kwargs)
    

    This technique can be useful for subclassing and writing wrapper functions.

    class Car:
        def __init__(self, color, mileage):
            self.color = color
            self.mileage = mileage
    
    class AlwaysBlueCar(Car):
        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            self.color = 'blue'
    
    >>> AlwaysBlueCar('green', 48392).color
    'blue'
    

    缺点就是接口不明确,传啥参数要看父类。所以一般不在自己写的类继承结构中(自己清楚接口的类)用。用在继承外部类(一些你不能控制、不清楚的类)中使用。但是在修饰器中,非常有用(维护性好)。

    import functools
    def trace(f):
        @functools.wraps(f)
        def decorated_function(*args, **kwargs):
            print(f, args, kwargs)
            result = f(*args, **kwargs)
            print(result)
        return decorated_function
    
    @trace
    def greet(greeting, name):
        return '{}, {}!'.format(greeting, name)
    
    >>> greet('Hello', 'Bob')
    <function greet at 0x1031c9158> ('Hello', 'Bob') {}
    'Hello, Bob!'
    
    With techniques like this one, it’s sometimes difficult to balance the
    idea of making your code explicit enough and yet adhere to the Don’t
    Repeat Yourself (DRY) principle. This will always be a tough choice to
    make. If you can get a second opinion from a colleague, I’d encourage
    you to ask for one.
    

    例子 * unpack iterable, ** unpack dict

    def print_vector(x, y, z):
        print('<%s, %s, %s>' % (x, y, z))
    
    tuple_vec = (1, 0, 1)
    print_vector(*tuple_vec)
    
    >>> genexpr = (x * x for x in range(3))
    >>> print_vector(*genexpr)
    
    dict_vec = {'y': 0, 'z': 1, 'x': 1}
    >>> print_vector(**dict_vec)
    <1, 0, 1>
    

    11.关于函数的return. Python是隐式return None

    默认return None

    def foo1(value):
        if value:
            return value
        else:
            return None
    
    def foo2(value):
        """Bare return statement implies `return None`"""
        if value:
            return value
        else:
            return
    
    def foo3(value):
        """Missing return statement implies `return None`"""
        if value:
            return value
    
    type(foo3(0))
    <class 'NoneType'>
    
    # 作者建议如果一个函数不用return, 就不写return None. 这是一个Python core feature.
    

    Chapter 4: Classes & OOP

    12. 对象比较"is" vs "=="

    is 比较引用是否指向同一个对象
    == 比值
    

    13. repr和str

    class Car:
        def __init__(self, color, mileage):
            self.color = color
            self.mileage = mileage
    
        def __repr__(self):
            return '__repr__ for Car'
    
        def __str(self):
            return '__str__ for Car'
    
    >>> my_car = Car('red', 37281)
    >>> print(my_car)
    __str__ for Car
    >>> '{}'.format(my_car)
    '__str__ for Car'
    >>> my_car
    __repr__ for Car
    
    Interestingly, containers like lists and dicts always use the result of
    __repr__ to represent the objects they contain. Even if you call str
    on the container itself:
    
    str([my_car])
    '[__repr__ for Car]'
    
    By the way, some people refer to Python’s “dunder” methods as
    “magic methods.” But these methods are not supposed to be magical
    in any way. The fact that these methods start and end in double
    underscores is simply a naming convention to flag them as core
    Python features.
    

    差异

    import datetime
    today = datetime.date.today()
    >>> str(today)
    '2017-02-02'
    
    >>> repr(today)
    'datetime.date(2017, 2, 2)'
    
    # __repr__ is for developers,  __str__ is for user. --- StackOverflow 
    

    应该如何用

    If you don’t add a __str__ method, Python falls back on the result
    of __repr__ when looking for __str__. Therefore, I recommend that
    you always add at least a __repr__ method to your classes.
    
    def __repr__(self):
        return (f'{self.__class__.__name__}('
                    f'{self.color!r}, {self.mileage!r})')
    
    >>> repr(my_car)
    'Car(red, 37281)'
    
    class Car:
        def __init__(self, color, mileage):
            self.color = color
            self.mileage = mileage
    
        def __repr__(self):
            return (f'{self.__class__.__name__}({self.color!r}, {self.mileage!r})')
    
        def __str__(self):
            return f'a {self.color} car'
    
    car = Car('Red', 44444)
    print(car)
    print(repr(car))
    


    14. Defining Your Own Exception Classes 自定义异常

    Why

    # 令stack trace更加清晰, 对debug更加友好
    # 更好维护
    # 例如比单纯地raise ValueError更好
    class NameTooShortError(ValueError):
        pass
    
    def validate(name):
        if len(name) < 10:
            raise NameTooShortError(name)
    
    print(validate('allen'))
    

    class BaseValidationError(ValueError):
        pass
    
    class NameTooShortError(BaseValidationError):
        pass
    
    class NameTooLongError(BaseValidationError):
        pass
    
    try:
        validate(name)
    except BaseValidationError as err:
        handle_validation_error(err)
    

    15. Cloning Objects. 复制(拷贝、克隆)对象

    对于mutable objects(可变对象), 有时需要克隆整个对象。意义在于修改克隆出来的对象,而不修改原来的对象。

    对于列表,字典,集合这些Python's built-in collections可以通过它们的工厂函数去克隆它们

    # 注意,这是shallow copies.
    new_list = list(original_list)
    new_dict = dict(original_dict)
    new_set = set(original_set)
    

    浅复制,深复制

    A shallow copy means constructing a new collection object and then
    populating it with references to the child objects found in the original.
    In essence, a shallow copy is only one level deep. The copying process
    does not recurse and therefore won’t create copies of the child objects
    themselves.
    

    浅复制例子

    >>> xs = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
    >>> ys = list(xs) # Make a shallow copy
    
    >>> xs
    [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
    >>> ys
    [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
    
    >>> xs.append(['new sublist'])
    >>> xs
    [[1, 2, 3], [4, 5, 6], [7, 8, 9], ['new sublist']]
    >>> ys
    [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
    
    >>> xs[1][0] = 'X'
    >>> xs
    [[1, 2, 3], ['X', 5, 6], [7, 8, 9], ['new sublist']]
    >>> ys
    [[1, 2, 3], ['X', 5, 6], [7, 8, 9]]
    

    深复制例子

    >>> import copy
    >>> xs = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
    >>> zs = copy.deepcopy(xs)
    
    >>> xs
    [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
    >>> zs
    [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
    
    >>> xs[1][0] = 'X'
    >>> xs
    [[1, 2, 3], ['X', 5, 6], [7, 8, 9]]
    >>> zs
    [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
    

    复制(浅复制,深复制)任意对象,包括自定义类

    浅复制对象

    # 在这个例子中,因为用primitive types(基本类型)int去定义坐标,所以这个例子中浅复制和深复制没区别
    import copy
    
    class Point:
        def __init__(self, x, y):
            self.x = x
            self.y = y
    
        def __repr__(self):
            return f'Point({self.x!r}, {self.y!r})'
    
    # 用copy.copy浅复制
    a = Point(23, 42)
    b = copy.copy(a)
    
    print(a is b)    #False
    print(id(a), id(b)) 
    

    深复制对象之前,再谈浅复制

    # 在这个例子中,Rectangle用Point去作为坐标,这样就需要深复制
    import copy
    
    class Point:
        def __init__(self, x, y):
            self.x = x
            self.y = y
    
        def __repr__(self):
            return f'Point({self.x!r}, {self.y!r})'
    
    class Rectangle:
        def __init__(self, topleft, bottomright):
            self.topleft = topleft
            self.bottomright = bottomright
    
        def __repr__(self):
            return (f'Rectangle({self.topleft!r}, {self.bottomright!r})')
    
    rect = Rectangle(Point(0, 1), Point(5, 6))
    # 浅复制
    shallow_rect = copy.copy(rect)
    
    print(rect is shallow_rect) # False
    print(rect.topleft is shallow_rect.topleft) # True
    
    >>> rect.topleft.x = 999
    >>> rect
    Rectangle(Point(999, 1), Point(5, 6))
    >>> srect
    Rectangle(Point(999, 1), Point(5, 6))
    

    深复制

    import copy
    
    class Point:
        def __init__(self, x, y):
            self.x = x
            self.y = y
    
        def __repr__(self):
            return f'Point({self.x!r}, {self.y!r})'
    
    class Rectangle:
        def __init__(self, topleft, bottomright):
            self.topleft = topleft
            self.bottomright = bottomright
    
        def __repr__(self):
            return (f'Rectangle({self.topleft!r}, {self.bottomright!r})')
    
    rect = Rectangle(Point(0, 1), Point(5, 6))
    shallow_rect = copy.deepcopy(rect)
    
    print(rect is shallow_rect) # False
    print(rect.topleft is shallow_rect.topleft) # False
    

    更深入复制

    看copy模块
    For example, objects can control
    how they’re copied by defining the special methods __copy__() and
    __deepcopy__() on them. 
    

    总结

    浅复制复制第一层,第一层独立。
    深复制复制所有层,完全和原来的对象独立。
    

    16. Abstract Base Classes 抽象类

    Why使用抽象类。如果没有抽象类,如何实现相关的接口。

    这个例子的坏处就是当调用的时候才得到error

    class Base:
        def foo(self):
            raise NotImplementedError()
    
        def bar(self):
            raise NotImplementedError()
    
    
    class Concrete(Base):
        def foo(self):
            return 'foo called'
    
    c = Concrete()
    c.foo()
    c.bar() # NotImplementedError
    

    更安全的做法是使用abc模块。令类继承结构更加可维护。

    from abc import ABCMeta, abstractclassmethod
    
    class Base(metaclass=ABCMeta):
        @abstractclassmethod
        def foo(self):
            pass
    
        @abstractclassmethod
        def bar(self):
            pass
    
    class Concrete(Base):
        def foo(self):
            pass
    
    c = Concrete()
    


    17. Namedtuples. 命名元组

    namedtuple是tuple的拓展。

    namedtuple解决tuple只能通过index访问、元祖内元素是随机的问题(例如很难去确定两个tuple拥有相同的元素和个数)。可提高可读性。

    from collections import namedtuple
    
    
    Car1 = namedtuple('Car1', 'color mileage')
    Car2 = namedtuple('Car2', [
        'Color',
        'mileage'
    ])
    
    
    my_car = Car1('red', 44444)
    
    # 特点1. 可通过元素名访问
    print(my_car.color)
    print(my_car.mileage)
    
    >>> color, mileage = my_car1
    >>> print(color, mileage)
    red 44444
    >>> print(*my_car)
    red 44444
    
    # 特点2. 和元组一样。是不可变的。
    >>> my_car.color = 'blue'
    AttributeError: "can't set attribute"
    

    namedtuples are a memoryefficient shortcut to defining an immutable class in Python manually.

    即namedtuple是基于class的。所以可以继承它。但这个例子little clunky

    Car = namedtuple('Car', 'color mileage')
    
    class MyCarWithMethods(Car):
        def hexcolor(self):
            if self.color == 'red':
                return '#ff0000'
            else:
                return '#000000'
    
    >>> c = MyCarWithMethods('red', 1234)
    >>> c.hexcolor()
    '#ff0000'
    
    #  It might be worth doing if you want a class with immutable properties, but it’s also easy to shoot yourself in the foot here
    

    The easiest way to create hierarchies of namedtuples is to use the base tuple’s _fields property:

    Car = namedtuple('Car', 'color mileage)
    ElectricCar = namedtuple('ElectricCar', Car._fields + ('charge',))
    
    >>> ElectricCar('red', 1234, 45.0)
    ElectricCar(color='red', mileage=1234, charge=45.0)
    

    built-in helper methods。 内置的工具方法

    namedtuple的built-in helper methods是以_开头。众所周知,_开头的方法是非公开接口。但是这里为了不和自定义的变量名冲突而在前面加_, 所以放心使用_开头的方法。它们是namedtuple的公共接口。

    >>> my_car._asdict()
    OrderedDict([('color', 'red'), ('mileage', 3812.4)])
    
    >>> json.dumps(my_car._asdict())
    '{"color": "red", "mileage": 3812.4}'
    
    >>> my_car._replace(color='blue')
    Car(color='blue', mileage=3812.4)
    
    >>> Car._make(['red', 999])
    Car(color='red', mileage=999)
    

    什么时候用

    Using namedtuples over unstructured tuples and dicts can also make
    my coworkers’ lives easier because they make the data being passed
    around “self-documenting” (to a degree).
    
    On the other hand, I try not to use namedtuples for their own sake if
    they don’t help me write “cleaner” and more maintainable code. Like
    many other techniques shown in this book, sometimes there can be
    too much of a good thing.
    
    However, if you use them with care, namedtuples can undoubtedly
    make your Python code better and more expressive.
    

    18. Class vs Instance Variable Pitfalls 类变量和实例变量的缺陷

    # Class variables are for data shared by all instances of a class.
    
    class Dog:
        num_legs = 4 # <- Class variable
    
        def __init__(self, name):
            self.name = name # <- Instance variable
    
    >>> jack = Dog('Jack')
    >>> jill = Dog('Jill')
    >>> jack.name, jill.name
    ('Jack', 'Jill')
    

    缺陷1

    实例中的__class__中的类变量不同步,因为这种行为会创造一个与类变量相同名字的变量(浅复制)

    >>> jack.num_legs, jack.__class__.num_legs
    (6, 4)
    

    缺陷2

    # Good
    class CountedObject:
        num_instances = 0
    
        def __init__(self):
            self.__class__.num_instances += 1
    
    >>> CountedObject.num_instances
    0
    >>> CountedObject().num_instances
    1
    >>> CountedObject().num_instances
    2
    >>> CountedObject().num_instances
    3
    >>> CountedObject.num_instances
    3
    
    # 这里有bug, 注意construtor
    class BuggyCountedObject:
        num_instances = 0
    
        def __init__(self):
            self.num_instances += 1 # !!!
    
    >>> BuggyCountedObject.num_instances
    0
    >>> BuggyCountedObject().num_instances
    1
    >>> BuggyCountedObject().num_instances
    1
    >>> BuggyCountedObject().num_instances
    1
    >>> BuggyCountedObject.num_instances
    0
    
    原因: “shadowed” the
    num_instance class variable by creating an instance variable of the
    same name in the constructor.
    

    19. Instance, Class, and Static Methods Demystified 实例方法、类方法、静态方法

    class MyClass:
        # self访问实例本身
        def method(self):
            return 'instance method called', self
    
        # cls访问类本身
        @classmethod
        def classmethod(cls):
            return 'class method called', cls
    
        @staticmethod
        def staticmethod():
            return 'static method called'
    
    m = MyClass()
    print(m.method())
    print(m.classmethod())
    print(m.staticmethod())
    

    Chapter 5 Common Data Structures in Python

  • 相关阅读:
    什么是交互式?
    python之禅
    爬虫保存cookies时重要的两个参数(ignore_discard和ignore_expires)的作用
    PL/0编译器(java version) – Symbol.java
    PL/0编译器(java version) – Scanner.java
    PL/0编译器(java version)–Praser.java
    PL/0编译器(java version)–PL0.java
    PL/0编译器(java version)–Pcode.java
    PL/0编译器(java version)
    PL/0编译器(java version)
  • 原文地址:https://www.cnblogs.com/allen2333/p/10400379.html
Copyright © 2011-2022 走看看