Python 进阶

  1. PEP8 编码规范, 及开发中的一些惯例和建议
  • 练习: 规范化这段代码
    from django.conf import settings
    from user.models import *
    import sys, os
    mod=0xffffffff
    def foo ( a , b = 123 ):
    c={ ‘x’ : 111 , ‘y’ : 222 }#定义一个字典
    d=[ 1 , 3,5 ]
    return a,b , c
    def bar(x):
    if x%2==0 : return True
  • 为什么要有编码规范
    • 编码是给人看的还是给机器看的?
    • 美观是重点吗?
      1. 美观
      2. 可读性
      3. 可维护性
      4. 健壮性
    • 团队内最好的代码状态: 所有人写出的代码像一个人写出来的
  • 代码编排:
    • 缩进 4 个空格, 禁止空格与 Tab 混用
    • 行长 80 字符: 防止单行逻辑过于复杂
  • import
    • 不要使用 from xxx import *
    • 0顺序
      1. 标准库
      2. 第三方库
      3. 自定义库
    • 单行不要 import 多个库
    • 模块内用不到的不要去 import
  • 空格
    • : , 后面跟一个空格, 前面无空格 (行尾分号后无空格)
    • 二元操作符前后各一个空格, 包括以下几类:
      1. 数学运算符: + - * / // = & |
      2. 比较运算符: == != > < >= <= is not in
      3. 逻辑运算符: and or not
      4. 位运算符: & | ^ << >>
    • 当 = 用于指示关键字参数或默认参数值时, 不要在其两侧使用空格
  • 适当添加空行
    • 函数间: 顶级函数间空 2 行, 类的方法之间空 1 行
    • 函数内: 同一函数内的逻辑块之间, 空 1 行
    • 文件结尾: 留一个空行 (Unix 中 \n 是文件的结束符)
  • 注释
    • 忌: 逐行添加注释, 没有一个注释
    • 行尾注释: 单行逻辑过于复杂时添加
    • 块注释: 一段逻辑开始时添加
    • 引入外来算法或者配置时须在注释中添加源连接, 标明出处
    • 函数、类、模块尽可能添加 docstring
  • 命名
    • 好的变量名要能做到“词能达意”
    • 除非在 lambda 函数中, 否则不要用 单字母 的变量名 (即使是 lambda 函数中的变量名也应该尽可能的有意义)
    • 包名、模块名、函数名、方法、普通变量名全部使用小写, 单词间用下划线连接
    • 类名、异常名使用 CapWords (首字母大写) 的方式, 异常名结尾加 Error 或 Wraning 后缀
    • 全局变量尽量使用大写, 一组同类型的全局变量要加上统一前缀, 单词用下划线连接
    • 函数名必须有动词, 最好是 do_something 的句式, 或者 somebody_do_something 句式
  • 语意明确、直白
    • not xx in yy VS xx not in yy
    • not a is b VS a is not b
  • 程序的构建
    • 函数是模块化思想的体现
    • 独立的逻辑应该抽离成独立函数,让代码结构更清晰,可复用度更高
    • 一个函数只做一件事情, 并把这件事做好
    • 大的功能用小函数之间灵活组合来完成
    • 避免编写庞大的程序, “大” 意味着体积庞大, 逻辑复杂甚至混乱
  • 自定义的变量名、函数名不要与标准库中的名字冲突
  • pip install pycodestyle pylint flake8 autopep8
    • 和 ** 的用法
  • 函数定义时接收不定长参数
    def foo(*args, **kwargs):
    pass
  • 参数传递
    def foo(x, y, z, a, b):
    print(x)
    print(y)
    print(z)
    print(a)
    print(b)
    lst = [1, 2, 3]
    dic = {‘a’: 22, ‘b’: 77}
    foo(*lst, **dic)
  • import * 语法
    • 文件 xyz.py
      all = (‘a’, ‘e’, ‘_d’)

      a = 123
      _b = 456
      c = 'asdfghjkl'
      _d = [1,2,3,4,5,6]
      e = (9,8,7,6,5,4)
      
    • 文件 abc.py
      from xyz import *
      print(a)
      print(_b)
      print©
      print(_d)
      print(e)

  1. Python 的赋值和引用
  • ==, is: == 判断的是值, is 判断的是内存地址 (即对象的id)

  • 小整数对象: [-5, 256]

  • copy, deepcopy 的区别

    • copy: 只拷贝表层元素
    • deepcopy: 在内存中重新创建所有子元素
  • 练习1: 说出执行结果

    def extendList(val, lst=[]):lst.append(val)return lstlist1 = extendList(10)
    list2 = extendList(123, [])
    list3 = extendList('a')
    
  • 练习2: 说出下面执行结果

    from copy import copy, deepcopy
    from pickle import dumps, loadsa = ['x', 'y', 'z']
    b = [a] * 3
    c = copy(b)
    d = deepcopy(b)
    e = loads(dumps(b, 4))b[1].append(999)
    b.append(777)c[1].append(999)
    c.append(555)d[1].append(999)
    d.append(333)e[1].append(999)
    e.append(111)
    
  • 自定义 deepcopy: my_deepcopy = lambda item: loads(dumps(item, 4))

  1. 迭代器, 生成器
  • 练习: 说出如下代码的打印结果
    >>> def foo():
    … print(111)
    … yield 222
    … print(333)
    … yield 444
    … print(555)

    >>> n = foo()
    >>> next(n)
    >>> next(n)
    >>> next(n)
    
  • generator: 生成器是一种特殊的迭代器, 不需要自定义 iternext

    • 生成器函数 (yield)

    • 生成器表达式

      class Range:
      def init(self, start, end=None, step=1):
      if end is None:
      self.end = start
      self.start = 0
      else:
      self.start = start
      self.end = end
      self.step = step

      def __iter__(self):return selfdef __next__(self):if self.start < self.end:current = self.startself.start += self.stepreturn currentelse:raise StopIteration()
      
  • iterator: 任何实现了 iternext 方法的对象都是迭代器.

    • iter 得到一个迭代器。迭代器的__iter__()返回自身
    • next 返回迭代器下一个值
    • 如果容器中没有更多元素, 则抛出 StopIteration 异常
    • Python2中没有 next(), 而是 next()
  • str / bytes / list / tuple / dict / set 自身不是迭代器,他们自身不具备 next(), 但是具有 iter(), iter() 方法用来把自身转换成一个迭代器

  • 练习1: 定义一个随机数迭代器, 随机范围为 [1, 50], 最大迭代次数 30
    import random

    class RandomIter:def __init__(self, start, end, times):self.start = startself.end = endself.count = timesdef __iter__(self):return selfdef __next__(self):self.count -= 1if self.count >= 0:return random.randint(self.start, self.end)else:raise StopIteration()
    
  • 练习2: 自定义一个迭代器, 实现斐波那契数列
    class Fib:
    def init(self, max_value):
    self.prev = 0
    self.curr = 1
    self.max_value = max_value

        def __iter__(self):return selfdef __next__(self):if self.curr <= self.max_value:res = self.currself.prev, self.curr = self.curr, self.prev + self.curr  # 为下一次做准备return reselse:raise StopIteration()
    
  • 练习3: 自定义一个生成器函数, 实现斐波那契数列
    def fib(max_value):
    prev = 0
    curr = 1
    while curr < max_value:
    yield curr
    prev, curr = curr, curr + prev

  • 迭代器、生成器有什么好处?

    • 节省内存
    • 惰性求值 (惰性求值思想来自于 Lisp 语言)
  • 各种推导式

    • 分三部分:生成值的表达式, 循环主体, 过滤条件表达式
    • 列表: [i * 3 for i in range(5) if i % 2 == 0]
    • 字典: {i: i + 3 for i in range(5)}
    • 集合: {i for i in range(5)}
  1. 装饰器
  • 最简装饰器
    def deco(func):
    def wrap(*args, **kwargs):
    return func(*args, **kwargs)
    return wrap

    @deco
    def foo(a, b):return a ** b
    
  • 原理

    • 对比被装饰前后的 foo.name 和 foo.doc
      from functools import wraps

      def deco(func):'''i am deco'''@wraps(func)  # 还原被装饰器修改的原函数属性def wrap(*args, **kwargs):'''i am wrap'''return func(*args, **kwargs)return wrap
      
    • 简单过程
      fn = deco(func)
      foo = fn
      foo(*args, **kwargs)

    • 多个装饰器叠加调用的过程
      @deco1
      @deco2
      @deco3
      def foo(x, y):
      return x ** y

      # 过程拆解 1
      fn3 = deco3(foo)
      fn2 = deco2(fn3)
      fn1 = deco1(fn2)
      foo = fn1
      foo(3, 4)# 过程拆解 2
      # 单行: deco1( deco2( deco3(foo) ) )(3, 2)
      deco1(deco2(deco3(foo))
      )(3, 4)
      
  • 带参数的装饰器
    def deco(n):
    def wrap1(func):
    def wrap2(*args, **kwargs):
    return func(*args, **kwargs)
    return wrap2
    return wrap1

    # 调用过程
    wrap1 = deco(n)
    wrap2 = wrap1(foo)
    foo = wrap2
    foo()# 单行形式
    check_result(30)(foo)(4, 8)
    
  • 装饰器类和 call
    class Deco:
    def init(self, func):
    self.func = func

        def __call__(self, *args, **kwargs):return self.func(*args, **kwargs)@Deco
    def foo(x, y):return x ** y# 过程拆解
    fn = Deco(foo)
    foo = fn
    foo(12, 34)
    
  • 使用场景

    • 参数、结果检查
    • 缓存、计数
    • 日志、统计
    • 权限管理
    • 重试
    • 其他
  • 练习1: 写一个 timer 装饰器, 计算出被装饰函数调用一次花多长时间, 并把时间打印出来
    import time
    from functools import wraps

    def timer(func):@wraps(func)  # 修正 docstringdef wrap(*args, **kwargs):time0 = time.time()result = func(*args, **kwargs)time1 = time.time()print(time1 - time0)return resultreturn wrap
    
  • 练习2: 写一个 Retry 装饰器
    import time

    class retry(object):def __init__(self, max_retries=3, wait=0, exceptions=(Exception,)):self.max_retries = max_retriesself.exceptions = exceptionsself.wait = waitdef __call__(self, func):def wrapper(*args, **kwargs):for i in range(self.max_retries + 1):try:result = func(*args, **kwargs)except self.exceptions:time.sleep(self.wait)continueelse:return resultreturn wrapper
    
  1. 函数闭包
  • Function Closure: 引用了自由变量的函数即是一个闭包. 这个被引用的自由变量和这个函数一同存在, 即使已经离开了创造它的环境也不例外.

  • 说出下面函数返回值
    def foo():
    l = []
    def bar(i):
    l.append(i)
    return l
    return bar

    f1 = foo()
    f2 = foo()# 说出下列语句执行结果
    f1(1)
    f1(2)
    f2(3)
    
  • 深入一点: object.closure

  • 作用域
    ┌───────────────────────────┐
    │ built-in namespace │
    ├───────────────────────────┤ ↑
    │ global namespace │
    │ ┌───────────────────────┤
    │ │ local namespace │ n = 123
    │ │ ┌───────────────────┤
    │ │ │ local namespace │ ↑
    │ │ │ ┌───────────────┤
    │ │ │ │ … │ print(n)
    └───┴───┴───┴───────────────┘

    • 声明全局变量: global
    • 声明非本层的 局部变量 : nonlocal
    • 查看全局变量: globals()
    • 查看局部变量: locals()
    • 查看变量: vars([object]) # 不传参数相当于 locals(), 传入对象后, 会得到 object.dict
  1. 类方法和静态方法
  • method

    • 通过实例调用
    • 可以引用类内部的任何属性和方法
  • classmethod

    • 无需实例化
    • 可以调用类属性和类方法
    • 无法取到普通的成员属性和方法
  • staticmethod

    • 无需实例化
    • 无法取到类内部的任何属性和方法, 完全独立的一个方法
  • 练习: 说出下面代码的运行结果
    class Test(object):
    x = 123

        def __init__(self):self.y = 456def bar1(self):print('i am a method')@classmethoddef bar2(cls):print('i am a classmethod')@staticmethoddef bar3():print('i am a staticmethod')def foo1(self):print(self.x)print(self.y)self.bar1()self.bar2()self.bar3()@classmethoddef foo2(cls):print(cls.x)print(cls.y)cls.bar1()cls.bar2()cls.bar3()@staticmethoddef foo3(obj):print(obj.x)print(obj.y)obj.bar1()obj.bar2()obj.bar3()t = Test()
    t.foo1()
    t.foo2()
    t.foo3()
    
  1. 继承相关问题
  • 什么是多态
    class Animal:
    def run(self):
    print(‘animal running’)

    class Lion(Animal):def run(self):print('lion running')class Tiger(Animal):def run(self):print('tiger running')class LeoTiger(Lion, Tiger):passcub = LeoTiger()
    isinstance(cub, Lion)
    isinstance(cub, Tiger)
    
  • 多继承

    • 方法和属性的继承顺序: Cls.mro()
    • 菱形继承问题
      继承关系示意
      菱形继承
      A.foo()
      /
      B C.foo()
      \ /
      D.mro() # 方法的继承顺序,由 C3 算法得到
  • Mixin: 通过单纯的 mixin 类完成功能组合

  • super
    class A:
    def init(self):
    print(‘enter A’)
    self.x = 111
    print(‘exit A’)

    class B(A):def __init__(self):print('enter B')self.y = 222A.__init__(self)# super().__init__()print('exit B')class C(A):def __init__(self):print('enter C')self.z = 333A.__init__(self)# super().__init__()print('exit C')class D(B, C):def __init__(self):print('enter D')B.__init__(self)C.__init__(self)# super().__init__()print('exit D')d = D()
    
  1. 垃圾收集 (GC)
  • Garbage Collection (GC)

  • 引用计数

    • 优点: 简单、实时性高

    • ==>

    • 缺点: 消耗资源、循环引用
      lst1 = [3, 4] # lst1->ref_count 1
      lst2 = [8, 9] # lst2->ref_count 1

      # lst1 -> [3, 4, lst2]
      lst1.append(lst2)       # lst2->ref_count 2# lst2 -> [8, 9, lst1]
      lst2.append(lst1)       # lst1->ref_count 2del lst1                # lst1->ref_count 1
      del lst2                # lst2->ref_count 1
      
  • 标记-清除, 分代收集

    • 用来回收引用计数无法清除的内存
  1. Python 魔术方法

  2. str 格式化输出对象

  3. initnew

    • new 创建一个实例,并返回类的实例
    • init 初始化实例,无返回值
    • new 是一个类方法
      • 单例模式
        class A(object):
        ‘’‘单例模式’’’
        obj = None
        def new(cls, *args, **kwargs):
        if cls.obj is None:
        cls.obj = object.new(cls)
        return cls.obj
  4. 数学运算、比较运算

    • 运算符重载

      • +: add(value)
      • -: sub(value)
      • *: mul(value)
      • /: truediv(value) (Python 3.x), div(value) (Python 2.x)
      • //: floordiv(value)
      • %: mod(value)
      • &: and(value)
      • |: or(value)
    • 练习: 实现字典的 add 方法, 作用相当于 d.update(other)
      class Dict(dict):
      def add(self, other):
      if isinstance(other, dict):
      new_dict = {}
      new_dict.update(self)
      new_dict.update(other)
      return new_dict
      else:
      raise TypeError(‘not a dict’)
    • 比较运算符的重载
      • ==: eq(value)
      • !=: ne(value)
      • : gt(value)

      • =: ge(value)

      • <: lt(value)
      • <=: le(value)
    • 练习: 完成一个类, 实现数学上无穷大的概念
      class Inf:
      def lt(self, other):
      return False
      def le(self, other):
      return False
      def ge(self, other):
      return True
      def gt(self, other):
      return True
      def eq(self, other):
      return False
      def ne(self, other):
      return True
  5. 容器方法

    • len -> len
    • iter -> for
    • contains -> in
    • getitem 对 string, bytes, list, tuple, dict 有效
    • setitem 对 list, dict 有效
  6. 可执行对象: call

  7. 上下文管理 with:

    • enter 进入 with 代码块前的准备操作

    • exit 退出时的善后操作

    • 文件对象、线程锁、socket 对象 等 都可以使用 with 操作

    • 示例
      class A:
      def enter(self):
      return self

          def __exit__(self, Error, error, traceback):print(args)
      
  8. setattr, getattribute, getattr, dict

    • 内建函数:setattr(), getattr(), hasattr()

    • 常用来做属性监听
      class User:
      ‘’‘TestClass’’’
      z = [7,8,9]
      def init(self):
      self.money = 10000
      self.y = ‘abc’

          def __setattr__(self, name, value):if name == 'money' and value < 0:raise ValueError('money < 0')print('set %s to %s' % (name, value))object.__setattr__(self, name, value)def __getattribute__(self, name):print('get %s' % name)return object.__getattribute__(self, name)def __getattr__(self, name):print('not has %s' % name)return -1def foo(self, x, y):return x ** y# 对比
      a = A()
      print(A.__dict__)
      print(a.__dict__)
      
  9. 槽: slots

    • 固定类所具有的属性
    • 实例不会分配 dict
    • 实例无法动态添加属性
    • 优化内存分配
      class A:
      slots = (‘x’, ‘y’)
  10. Python 性能之困

  11. 计算密集型

    • CPU 长时间满负荷运行, 如图像处理、大数据运算、圆周率计算等
    • 计算密集型: 用 C 语言补充
    • cProfile, timeit
  12. I/O 密集型

    • 网络 IO, 文件 IO, 设备 IO 等
    • 一切皆文件
  13. 多任务处理

    • 进程、线程、协程调度的过程叫做上下文切换
    • 进程、线程、协程对比
      名称 资源占用 数据通信 上下文切换 (Context)
      进程 大 不方便 (网络、共享内存、管道等) 操作系统按时间片切换, 不够灵活, 慢
      线程 小 非常方便 按时间片切换, 不够灵活, 快
      协程 非常小 非常方便 根据I/O事件切换, 更加有效的利用 CPU
  14. 全局解释器锁 ( GIL )

    • 它确保任何时候一个进程中都只有一个 Python 线程能进入 CPU 执行。
    • 全局解释器锁造成单个进程无法使用多个 CPU 核心
    • 通过多进程来利用多个 CPU 核心,一般进程数与CPU核心数相等,或者CPU核心数两倍
  15. 什么是同步、异步、阻塞、非阻塞?

    • 同步, 异步: 客户端调用服务器接口时
    • 阻塞, 非阻塞: 服务端发生等待
    • 阻塞 -> 非阻塞
    • 同步 -> 异步
  16. 协程:Stackless / greenlets / gevent | tornado / asyncio
    import asyncio

    async def foo(n):for i in range(10):print('wait %s s' % n)await asyncio.sleep(n)return itask1 = foo(1)
    task2 = foo(1.5)
    tasks = [asyncio.ensure_future(task1),asyncio.ensure_future(task2)]loop = asyncio.get_event_loop()  # 事件循环,协程调度器
    loop.run_until_complete( asyncio.wait(tasks) )
    
  17. 线程安全, 锁

    • 获得锁之后, 一定要释放, 避免死锁
    • 尽量使用 with 去操作锁
    • 获得锁之后, 执行的语句, 只跟被锁资源有关
    • 线程之间的数据交互尽量使用 Queue
  18. 一些技巧和误区

  19. 格式化打印 json

    • 调试时数据格式化:json.dumps(data, indent=4, sort_keys=True, ensure_ascii=False)
    • 传输时 json 压缩: json.dumps(data, ensure_ascii=False, separators=[’,’,’:’])
  20. 确保能取到有效值

    • d.get(k, default) 无值时使用默认值,对原字典无修改
    • d.setdefault 无值时使用默认值,并将默认值写入原字典
    • x = a if foo() else b
    • a or b
  21. try…except… 的滥用

    • 不要把所有东西全都包住, 程序错误需要报出来
    • 使用 try…except 要指明具体错误, try 结构不是用来隐藏错误的, 而是用来有方向的处理错误的
  22. 利用 dict 做模式匹配
    def do1():
    print(‘i am do1’)

    def do2():print('i am do2')def do3():print('i am do3')def do4():print('i am do4')mapping = {1: do1, 2: do2, 3: do3, 4: do4}
    mod = random.randint(1, 10)
    func = mapping.get(mod, do4)
    func()
    
  23. inf, -inf, nan

  24. 字符串拼接尽量使用 join 方式: 速度快, 内存消耗小

  25. property: 把一个方法属性化
    class C(object):
    @property
    def x(self):
    “I am the ‘x’ property.”
    return self._x

  26. else 子句: if, for, while, try

Python 进阶 之 知识点记忆相关推荐

  1. 知识点 —— Python进阶-3

    Python进阶–3 单例模式 常用开发模式之一 用途 确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时 比如,某个服务器程序的配置信息存放在一个文件中,客户端通过一个 A ...

  2. 知识点 —— Python进阶-2

    Python进阶–2 面向对象–基础 引入理解 https://blog.csdn.net/shenzixincaiji/article/details/83576687 类.对象.属性.方法 基础 ...

  3. 知识点 —— Python进阶-1

    Python进阶–1 装饰器 作用 保证在不改变原有函数结构的基础上,实现其功能的添加 即开放封闭原则: 开放:可以增加新的功能 封闭:原函数的代码不能修改 可应用的场景 引入日志 函数执行时间统计 ...

  4. Python进阶6——序列操作

    1.序列的拼接和复制 Python中使用+对序列进行拼接,使用*对序列进行复制 s=str(1234) l=list(range(2,13)) print(s,l) print('---------- ...

  5. Python 进阶_生成器 生成器表达式

    目录 目录 相关知识点 生成器 生成器 fab 的执行过程 生成器和迭代器的区别 生成器的优势 加强的生成器特性 生成器表达式 生成器表达式样例 小结 相关知识点 Python 进阶_迭代器 & ...

  6. Python 进阶之路 (十二) 尾声即是开始

    Python进阶之路总结 大家好,我的<< Python进阶之路>>到这一期就到此为止了,和 <<Python 基础起步>>不同,在掌握了一些基础知识后 ...

  7. 【Python进阶】Python进阶专栏、编程与开源框架知识星球上线,等你来follow

    大家好,今天我将在有三AI开设新专栏<Python进阶>.在这个专栏中,我们会讲述Python的各种进阶操作,包括Python对文件.数据的处理,Python各种好用的库如NumPy.Sc ...

  8. python进阶记录之基础篇二十六_Python进阶记录之基础篇(十六)

    回顾 在Python进阶记录之基础篇(十五)中,我们介绍了面向对象的基本概念以及Python中类和对象的基础知识,需要重点掌握类的创建和对象的使用.今天我们继续讲一下Python中面向对象的相关知识点 ...

  9. python 注释一段话_干货!Python入门基础知识点总结

    或看好Python的广阔前景,或看中Python的语法简洁,越来越多零基础的人选择学Python.但是Python基础知识有哪些呢?且看我的分析. Python部分基础知识点汇总 数据类型:编程中操作 ...

最新文章

  1. 做一个简单的网上交易系统(参考淘宝、天猫的交易系统)
  2. 从实例入手Shiro并实现HellloWorld
  3. Google Maps地图投影全解析
  4. 给创业者的30条建议
  5. 谈谈Java虚拟机——Class文件结构
  6. 有史以来最伟大的软件开发书籍(国外篇)
  7. 微信和简书输入框文本选择手柄小bug
  8. 【锁】redis加锁的几种方法
  9. qt.qpa.plugin: Could not load the Qt platform plugin “xcb“ in问题
  10. 新网银行模型竞赛点评-小微风控算法大赛-早期风险识别
  11. 地图上如何量方位角_利用GPS测量方位角的方法
  12. 目标检测数据集PASCAL VOC详解
  13. w3af 安装并配置桌面和快速启动栏
  14. 大数据学习之HBase入门笔记
  15. C语言结构、联合、位操作、位域和枚举
  16. TortoiseGit(小乌龟) git did not exit cleanly (exit code 1)
  17. STM32 自定义HID USB设备的实现
  18. 检测是否是ie浏览器及ie版本号
  19. Kubernetes网络三部曲之三 ~ NodePort vs LoadBalancer vs Ingress
  20. java第二个案例:运动员和教练(巩固理解抽象类和接口、继承等知识点)

热门文章

  1. 高速PCB设计指南(十六)
  2. Vue中better-scroll的使用
  3. 《C游记》 第叁章 - 一朝函数思习得 模块思维世间生(壹)
  4. (Select)解决:动态获取从 [Element-ui] 的 Select (选择器)选中的 label 值得 id 与 name 等所有属性值
  5. 51单片机:动态数码管
  6. 真实线上OOM溢出借助MAT工具排查(原创)
  7. 测试报告参考规范之测试结果和分析
  8. 背英语单词的方法,技巧
  9. 面试 C++ 程序员,什么样的问题是好问题?
  10. JavaScript算法 — 零钱兑换问题