简介

*args 和 **kwargs

  • *是必要的, 后面的变量可以变换

  • *args 是用来发送一个非键值对的可变数量的参数列表给一个函数.

    def test_var_args(f_arg, *argv):
        print("first normal arg:", f_arg)
        for arg in argv:
            print("another arg through *argv:", arg)
    
    test_var_args('yasoob', 'python', 'eggs', 'test')
    
  • **kwargs允许你将不定长度的键值对, 作为参数传递给一个函数.

    def greet_me(**kwargs):
        for key, value in kwargs.items():
            print("{0} == {1}".format(key, value))
    
    >>> greet_me(name="yasoob")
    name == yasoob
    
  • 如果你想在函数里同时使用所有这三种参数

    some_func(fargs, *args, **kwargs)
    

生成器(Generators)

  • yield

    • 生成器也是一种迭代器,但是你只能对其迭代一次。这是因为它们并没有把所有的值存在内存中,而是在运行时生成值。
    • 你通过遍历来使用它们,要么用一个“for”循环,要么将它们传递给任意可以进行迭代的函数和结构。
    def generator_function():
        for i in range(10):
            yield i
    
    for item in generator_function():
        print(item)
    
    # Output: 0
    # 1
    # 2
    # 3
    # 4
    # 5
    # 6
    # 7
    # 8
    # 9
    
  • 许多Python 2里的标准库函数都会返回列表,而Python 3都修改成了返回生成器,因为生成器占用更少的资源。

Map, Filter, Reduce

  • Map: 会将一个函数映射到一个输入列表的所有元素上。

    map(function_to_apply, list_of_inputs)
    
    items = [1, 2, 3, 4, 5]
    squared = list(map(lambda x: x**2, items))
    
  • Filter: 过滤列表中的元素,并且返回一个由所有符合要求的元素所构成的列表

    number_list = range(-5, 5)
    less_than_zero = filter(lambda x: x < 0, number_list)
    print(list(less_than_zero))  
    # 译者注:上面print时,加了list转换,是为了python2/3的兼容性
    #        在python2中filter直接返回列表,但在python3中返回迭代器
    #        因此为了兼容python3, 需要list转换一下
    
    # Output: [-5, -4, -3, -2, -1]
    
  • Reduce: 当需要对一个列表进行一些计算并返回结果时使用

    from functools import reduce
    product = reduce( (lambda x, y: x * y), [1, 2, 3, 4] )
    
    # Output: 24
    

Set数据结构

  • set与列表(list)的行为类似,区别在于set不能包含重复的值.

  • 检查列表中是否包含重复的元素

    """
    For循环
    """
    some_list = ['a', 'b', 'c', 'b', 'd', 'm', 'n', 'n']
    duplicates = []
    for value in some_list:
        if some_list.count(value) > 1:
            if value not in duplicates:
                duplicates.append(value)
    print(duplicates)
    ### 输出: ['b', 'n']
    
    """
    set
    """
    some_list = ['a', 'b', 'c', 'b', 'd', 'm', 'n', 'n']
    duplicates = set([x for x in some_list if some_list.count(x) > 1])
    print(duplicates)
    ### 输出: set(['b', 'n'])
    
  • 交集(intersection): 你对比两个集合的交集(两个集合中都有的数据)

    valid = set(['yellow', 'red', 'blue', 'green', 'black'])
    input_set = set(['red', 'brown'])
    print(input_set.**intersection**(valid))
    ### 输出: set(['red'])
    
  • 差集(difference): 找出无效的数据,相当于用一个集合减去另一个集合的数据

    valid = set(['yellow', 'red', 'blue', 'green', 'black'])
    input_set = set(['red', 'brown'])
    print(input_set.**difference**(valid))
    ### 输出: set(['brown'])
    
  • 创建集合可以用{}, 不用set()

三元运算符

is_fat = True
state = "fat" if is_fat else "not fat"

装饰器

  • 原始版本

    def a_new_decorator(a_func):
        def wrapTheFunction():
            print("before a_func()")
            a_func()
            print("after a_func()")
        return wrapTheFunction
    
    def a_function_requiring_decoration():
        print("smell")
    
    a_function_requiring_decoration()
    #outputs: "smell"
    
    a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)
    #now a_function_requiring_decoration is wrapped by wrapTheFunction()
    
    a_function_requiring_decoration()
    #outputs:before a_func()
    #        smell
    #        after a_func()
    
  • 装饰器版本

    # from functools import wraps
    
    def a_new_decorator(a_func):
    		# @wraps(a_func)
        def wrapTheFunction():
            print("before a_func()")
            a_func()
            print("after a_func()")
        return wrapTheFunction
    
    @a_new_decorator
    def a_function_requiring_decoration():
        print("smell")
    
    a_function_requiring_decoration()
    #outputs:before a_func()
    #        smell
    #        after a_func()
    
    • 存在问题, 不显示原函数名

      print(a_function_requiring_decoration.__name__)
      # Output: wrapTheFunction
      

      添加 @wraps(a_func) (@wraps接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等的功能。)

      print(a_function_requiring_decoration.__name__)
      # Output: a_function_requiring_decoration
      
    • wrap 日志: 日志是装饰器运用的另一个亮点

      from functools import wraps
      
      def logit(func):
          @wraps(func)
          def with_logging(*args, **kwargs):
              print(func.__name__ + " was called")
              return func(*args, **kwargs)
          return with_logging
      
      @logit
      def addition_func(x):
         """Do some math."""
         return x + x
      
      result = addition_func(4)
      # Output: addition_func was called
      
      • 还有详细logit构建, 发邮件啥的

对象变动 Mutation

  • 不可变(immutable)对象
    • int, string, float, tuple
  • 可变(mutation)对象
    • dict, list, set

slots

  • 默认情况下Python用一个字典来保存一个对象的实例属性。这非常有用,因为它允许我们在运行时去设置任意的新属性。
  • 然而,对于有着已知属性的小类来说,它可能是个瓶颈。这个字典浪费了很多内存。
  • 使用__slots__来告诉Python不要使用字典,而且只给一个固定集合的属性分配空间。
class MyClass(object):
  __slots__ = ['name', 'identifier']  # 使用slots减少内存占用
  def __init__(self, name, identifier):
      self.name = name
      self.identifier = identifier
      self.set_up()
  # ...

虚拟环境(virtualenv)

pip install virtualenv
virtualenv myproject
source myproject/bin/activate
  • 如果你想让你的virtualenv使用系统全局模块

    virtualenv --system-site-packages mycoolproject
    
  • 退出虚拟环境

    deactivate
    

容器 Collections

  1. defaultdict
    • 与dict不同, 你不需要检查key是否存在.
    • 当你在一个字典中对一个键进行嵌套赋值时,如果这个键不存在,会触发keyError异常。 defaultdict允许我们用一个聪明的方式绕过这个问题。
  2. counter
    • Counter是一个计数器,它可以帮助我们针对某项数据进行计数。

      from collections import Counter
      
      colours = (
          ('Yasoob', 'Yellow'),
          ('Ali', 'Blue'),
          ('Arham', 'Green'),
          ('Ali', 'Black'),
          ('Yasoob', 'Red'),
          ('Ahmed', 'Silver'),
      )
      
      favs = Counter(name for name, colour in colours)
      print(favs)
      
      ## 输出:
      ## Counter({
      ##     'Yasoob': 2,
      ##     'Ali': 2,
      ##     'Arham': 1,
      ##     'Ahmed': 1
      ##  })
      
    • 统计文件

      with open('filename', 'rb') as f:
          line_count = Counter(f)
      print(line_count)
      
    • deque

      • deque提供了一个双端队列,你可以从头/尾两端添加或删除元素.

      • 可以像list一样用

        from collections import deque
        d = deque()
        d.append(1)
        print(d[0])
        d.popleft()  # pop最左边
        d.pop()  # pop最右边
        d = deque(maxlen=30) # 设置最大长度, deque塞满时, 最左的元素自动删除
        
      • 你还可以从任一端扩展这个队列中的数据:

        d = deque([1,2,3,4,5])
        d.extendleft([0])  # 左边添加
        d.extend([6,7,8])  # 右边添加
        print(d)
        
        ## 输出: deque([0, 1, 2, 3, 4, 5, 6, 7, 8])
        
    • 命名元组 (namedtuple)

      from collections import namedtuple
      
      Animal = namedtuple('Animal', 'name age type')
      perry = Animal(name="perry", age=31, type="cat")
      
      print(perry)
      ## 输出: Animal(name='perry', age=31, type='cat')
      print(perry.name)
      ## 输出: 'perry'
      print(perry[0])
      ## 输出: perry
      ##############
      perry.age = 42  # x
      ## 输出:
      ## Traceback (most recent call last):
      ##     File "", line 1, in
      ## AttributeError: can't set attribute
      ###############
      print(perry._asdict())  # 将一个命名元组转换为字典
      ## 输出: OrderedDict([('name', 'Perry'), ('age', 31), ...
      

枚举 Enumerate

for counter, value in enumerate(some_list):
    print(counter, value)

my_list = ['apple', 'banana', 'grapes', 'pear']
for c, value in enumerate(my_list, 1):  # 这个可选参数允许我们定制从哪个数字开始枚举
    print(c, value)
# 输出:
(1, 'apple')
(2, 'banana')
(3, 'grapes')
(4, 'pear')

对象自省

  • dir: 返回一个列表,列出了一个对象所拥有的属性和方法。

    my_list = [1, 2, 3]
    dir(my_list)
    # Output: ['__add__', '__class__', '__contains__', '__delattr__', '__delitem__',
    # '__delslice__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
    # '__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__',
    # '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__',
    # '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__',
    # '__setattr__', '__setitem__', '__setslice__', '__sizeof__', '__str__',
    # '__subclasshook__', 'append', 'count', 'extend', 'index', 'insert', 'pop',
    # 'remove', 'reverse', 'sort']
    
  • type: 返回一个对象的类型

  • id: 返回任意不同种类对象的唯一ID, 内存地址.

  • inspect: 获取活跃对象的信息

    import inspect
    print(inspect.getmembers(str))
    # Output: [('__add__', <slot wrapper '__add__' of ... ...
    

列表推导式, 字典推导式, 集合推导式

  • list

    variable = [out_exp for out_exp in input_list if out_exp == 2]
    squared = [x**2 for x in range(10)]
    
  • dict

    # 把同一个字母但不同大小写的值合并起来
    mcase = {'a': 10, 'b': 34, 'A': 7, 'Z': 3}
    mcase_frequency = {
        k.lower(): mcase.get(k.lower(), 0) + mcase.get(k.upper(), 0)
        for k in mcase.keys()
    }
    # mcase_frequency == {'a': 17, 'z': 3, 'b': 34}
    
    # 快速对换一个字典的键和值
    {v: k for k, v in some_dict.items()}
    
  • squared

    squared = {x**2 for x in [1, 1, 2]}
    print(squared)
    # Output: {1, 4}
    

异常

  • try except

    try:
        file = open('test.txt', 'rb')
    except IOError as e: # 处理一个IOError的异常
        print('An IOError occurred. {}'.format(e.args[-1]))
    
    try:
        file = open('test.txt', 'rb')
    except EOFError as e:
        print("An EOF error occurred.")
        raise e
    except IOError as e:
        print("An error occurred.")
        raise e
    
    try:
        file = open('test.txt', 'rb')
    except Exception: # 捕获所有异常
        # 打印一些异常日志,如果你想要的话
        raise
    
  • finally从句: 包裹到finally从句中的代码不管异常是否触发都将会被执行.

    try:
        file = open('test.txt', 'rb')
    except IOError as e:
        print('An IOError occurred. {}'.format(e.args[-1]))
    finally:
        print("This would be printed whether or not an exception occurred!")
    
    # Output: An IOError occurred. No such file or directory
    # This would be printed whether or not an exception occurred!
    
  • try/else从句: 没有触发异常时运行

    try:
        print('I am sure no exception is going to occur!')
    except Exception:
        print('exception')
    else:
        # 这里的代码只会在try语句里没有触发异常时运行,
        # 但是这里的异常将 *不会* 被捕获
        print('This would only run if no exception occurs. And an error here '
              'would NOT be caught.')
    finally:
        print('This would be printed in every case.')
    
    # Output: I am sure no exception is going to occur!
    # This would only run if no exception occurs.
    # This would be printed in every case.
    

lambda表达式

lambda 参数:操作(参数)
add = lambda x, y: x + y
print(add(3, 5))
# Output: 8

for-else

  • else从句会在循环正常结束时执行
for item in container:
    if search_something(item):
        # Found it!
        process(item)
        break
else:
    # Didn't find anything..
    not_found_in_container()

使用C扩展

CTypes

from ctypes import *

Open函数

with open('photo.jpg', 'r+') as f:
    jpgdata = f.read()
  • 读取一个文件,检测它是否是JPG(提示:这些文件头部以字节FF D8开始)
with open('photo.jpg', 'rb') as inf:
    jpgdata = inf.read()

if jpgdata.startswith(b'\xff\xd8'):
    text = u'This is a JPEG file (%d bytes long)\n'
else:
    text = u'This is a random file (%d bytes long)\n'

函数缓存

  • 设置最多缓存最近多少个返回值
  • 缓存多的话, 可重复调用的值, 节省运行时间
  • cache_info() 用于查看缓存信息
from functools import lru_cache

@lru_cache(maxsize=32)  # 最好设置为 2 的倍数,默认为 128。None 的话就相当于是 maxsize 为正无穷了
def fib(n):
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)

>>> print([fib(n) for n in range(10)])
# Output: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

fib.cache_clear()  # 对返回值清空缓存

上下文管理器

class File(object):
    def __init__(self, file_name, method):
        self.file_obj = open(file_name, method)
    def __enter__(self):
        return self.file_obj
    def __exit__(self, type, value, traceback):
        self.file_obj.close()

with File('demo.txt', 'w') as opened_file:
    opened_file.write('Hola!')
  • 当异常发生时,with语句会采取哪些步骤。
    1. 它把异常的type,value和traceback传递给__exit__方法
    2. 它让__exit__方法来处理异常
    3. 如果__exit__返回的是True,那么这个异常就被优雅地处理了。
    4. 如果__exit__返回的是True以外的任何东西,那么这个异常将被with语句抛出。

bisect 数组二分查找算法

这个模块对有序列表提供了支持,使得他们可以在插入新数据仍然保持有序。对于长列表,如果其包含元素的比较操作十分昂贵的话,这可以是对更常见方法的改进。

bisect.**bisect_left**(a, x, lo=0, hi=len(a), ***, key=None)

在 a 中找到 x 合适的插入点以维持有序。参数 lo 和 hi 可以被用于确定需要考虑的子集;默认情况下整个列表都会被使用。如果 x 已经在 a 里存在,那么插入点会在已存在元素之前(也就是左边)。如果 a 是列表(list)的话,返回值是可以被放在 list.insert() 的第一个参数的。 返回的插入点 i 将数组 a 分成两半,使得 all(val < x for val in a[lo : i]) 在左半边而 all(val >= x for val in a[i : hi]) 在右半边。 key 指定带有单个参数的 key function 用来从数组的每个元素中提取比较键。 为了支持搜索复杂记录,键函数不会被应用到 x 值。 如果 key 为 None,则将直接进行元素比较而不需要中间的函数调用。 在 3.10 版更改: 增加了 key 形参。

python中的特殊方法

python中的特殊方法被python解释器调用, 我们一般不会亲自调用.

我们可以使用内置函数来使用. eg: __len()__实现后, 使用len()即可.

也有特殊方法的调用是隐式的. eg: for i in x, 使用的是内置函数iter(x).

详细见:

Python基础-特殊方法

  • 特殊(魔术)方法汇总一览表