GitHub 上有一个名为《What the f*ck Python!》的项目,这个有趣的项目意在收集 Python 中那些难以理解和反人类直觉的例子以及鲜为人知的功能特性,并尝试讨论这些现象背后真正的原理!

原版地址:https://github.com/satwikkansal/wtfpython

最近,一位名为“暮晨”的贡献者将其翻译成了中文。

中文版地址:https://github.com/leisurelicht/wtfpython-cn

上一篇 Python:What the f*ck Python(上)

原本每个的标题都是原版中的英文,有些取名比较奇怪,不直观,我换成了可以描述主题的中文形式,有些是自己想的,不足之处请指正。另外一些 Python 中的彩蛋被我去掉了。

我将所有代码都亲自试过了,加入了一些自己的理解和例子,所以会和原文稍有不同。

21. 子类关系

  1. >>> from collections import Hashable
  2. >>> issubclass(list, object)
  3. True
  4. >>> issubclass(object, Hashable)
  5. True
  6. >>> issubclass(list, Hashable)
  7. False

子类关系应该是可传递的,对吧?即,如果 AB 的子类,BC 的子类,那么 A 应该 是 C 的子类。

说明:

  • Python 中的子类关系并不必须是传递的,任何人都可以在元类中随意定义 __subclasscheck__
  • issubclass(cls, Hashable) 被调用时,它只是在 cls 中寻找 __hash__() 方法或继承自 __hash__() 的方法。
  • 由于 object 是可散列的(hashable),而 list 是不可散列的,所以它打破了这种传递关系。

22. 神秘的键型转换

  1. class SomeClass(str):
  2. pass
  3. some_dict = {'s': 42}

Output:

  1. >>> type(list(some_dict.keys())[0])
  2. <class 'str'>
  3. >>> s = SomeClass('s')
  4. >>> some_dict[s] = 40
  5. >>> some_dict # 预期: 两个不同的键值对
  6. {'s': 40}
  7. >>> type(list(some_dict.keys())[0])
  8. <class 'str'>

说明:

  • 由于 SomeClass 会从 str 自动继承 __hash__() 方法,所以 s 对象和 's' 字符串的哈希值是相同的。
  • SomeClass('s') == 's'True 是因为 SomeClass 也继承了 str__eq__() 方法。
  • 由于两者的哈希值相同且相等,所以它们在字典中表示相同的键。

如果想要实现期望的功能, 我们可以重定义 SomeClass__eq__() 方法.

  1. class SomeClass(str):
  2. def __eq__(self, other):
  3. return (
  4. type(self) is SomeClass
  5. and type(other) is SomeClass
  6. and super().__eq__(other)
  7. )
  8. # 当我们自定义 __eq__() 方法时, Python 不会再自动继承 __hash__() 方法
  9. # 所以我们也需要定义它
  10. __hash__ = str.__hash__
  11. some_dict = {'s':42}

Output:

  1. >>> s = SomeClass('s')
  2. >>> some_dict[s] = 40
  3. >>> some_dict
  4. {'s': 40, 's': 42}
  5. >>> keys = list(some_dict.keys())
  6. >>> type(keys[0]), type(keys[1])
  7. <class 'str'> <class '__main__.SomeClass'>

23. 链式赋值表达式

  1. >>> a, b = a[b] = {}, 5
  2. >>> a
  3. {5: ({...}, 5)}

说明:

根据 Python 语言参考,赋值语句的形式如下:

  1. (target_list "=")+ (expression_list | yield_expression)

赋值语句计算表达式列表(expression list)(请记住,这可以是单个表达式或以逗号分隔的列表,后者返回元组)并将单个结果对象从左到右分配给目标列表中的每一项。

(target_list "=")+ 中的 + 意味着可以有一个或多个目标列表。在这个例子中,目标列表是 a, ba[b]。表达式列表只能有一个,是 {}, 5

这话看着非常的晦涩,我们来看一个简单的例子:

  1. a, b = b, c = 1, 2
  2. print(a, b, c)

Output:

  1. 1 1 2

在这个简单的例子中,目标列表是 a, bb, c,表达式是 1, 2。将表达式从左到右赋给目标列表,上述例子就可以拆分成:

  1. a, b = 1, 2
  2. b, c = 1, 2

所以结果就是 1 1 2

那么,原例子就不难理解了,拆解开来就是:

  1. a, b = {}, 5
  2. a[b] = a, b

这里不能写作 a[b] = {}, 5,因为这样第一句中的 {} 和第二句中的 {} 其实就是不同的对象了,而实际他们是同一个对象。这就形成了循环引用,输出中的 {...} 指与 a 引用了相同的对象。

我们来验证一下:

  1. >>> a[b][0] is a
  2. True

可见确实是同一个对象。

以下是一个简单的循环引用的例子:

  1. >>> some_list = some_list[0] = [0]
  2. >>> some_list
  3. [[...]]
  4. >>> some_list[0]
  5. [[...]]
  6. >>> some_list is some_list[0]
  7. True
  8. >>> some_list[0][0][0][0][0][0] == some_list
  9. True

24. 空间移动

  1. import numpy as np
  2. def energy_send(x):
  3. # 初始化一个 numpy 数组
  4. np.array([float(x)])
  5. def energy_receive():
  6. # 返回一个空的 numpy 数组
  7. return np.empty((), dtype=np.float).tolist()

Output:

  1. >>> energy_send(123.456)
  2. >>> energy_receive()
  3. 123.456

说明:

energy_send() 函数中创建的 numpy 数组并没有返回,因此内存空间被释放并可以被重新分配。

numpy.empty() 直接返回下一段空闲内存,而不重新初始化。而这个内存点恰好就是刚刚释放的那个(通常情况下,并不绝对)。

25. 不要混用制表符(tab)和空格(space)

tab 是 8 个空格,而用空格表示则一个缩进是 4 个空格,混用就会出错。python3 里直接不允许这种行为了,会报错:

TabError: inconsistent use of tabs and spaces in indentation

很多编辑器,例如 pycharm,可以直接设置 tab 表示 4 个空格。

26. 迭代字典时的修改

  1. x = {0: None}
  2. for i in x:
  3. del x[i]
  4. x[i+1] = None
  5. print(i)

Output(Python 2.7- Python 3.5):

  1. 0
  2. 1
  3. 2
  4. 3
  5. 4
  6. 5
  7. 6
  8. 7

说明:

Python 不支持 对字典进行迭代的同时修改它,它之所以运行 8 次,是因为字典会自动扩容以容纳更多键值(译: 应该是因为字典的初始最小值是8,扩容会导致散列表地址发生变化而中断循环)。

在不同的 Python 实现中删除键的处理方式以及调整大小的时间可能会有所不同,python3.6 开始,到 5 就会扩容。

而在 list 中,这种情况是允许的,listdict 的实现方式是不一样的,list 虽然也有扩容,但 list 的扩容是整体搬迁,并且顺序不变。

  1. list = [1]
  2. j = 0
  3. for i in list:
  4. print(i)
  5. list.append(i + 1)

这个代码可以一直运行下去直到 int 越界。但一般不建议在迭代的同时修改 list

27. _del_

  1. class SomeClass:
  2. def __del__(self):
  3. print("Deleted!")

Output:

  1. >>> x = SomeClass()
  2. >>> y = x
  3. >>> del x # 这里应该会输出 "Deleted!"
  4. >>> del y
  5. Deleted!

说明:

del x 并不会立刻调用x.__del__(),每当遇到del xPython 会将 x 的引用数减 1,当 x 的引用数减到 0 时就会调用x.__del__()

我们再加一点变化:

  1. >>> x = SomeClass()
  2. >>> y = x
  3. >>> del x
  4. >>> y # 检查一下y是否存在
  5. <__main__.SomeClass instance at 0x7f98a1a67fc8>
  6. >>> del y # 像之前一样,这里应该会输出 "Deleted!"
  7. >>> globals() # 好吧, 并没有。让我们看一下所有的全局变量
  8. Deleted!
  9. {'__builtins__': <module '__builtin__' (built-in)>, 'SomeClass': <class __main__.SomeClass at 0x7f98a1a5f668>, '__package__': None, '__name__': '__main__', '__doc__': None}

y.__del__()之所以未被调用,是因为前一条语句(>>> y)对同一对象创建了另一个引用,从而防止在执行del y后对象的引用数变为 0。(这其实是 Python 交互解释器的特性,它会自动让 _ 保存上一个表达式输出的值。)

调用globals()导致引用被销毁,因此我们可以看到 Deleted! 终于被输出了。

28. 迭代列表时删除元素

在前面我附加了一个迭代列表时添加元素的例子,现在来看看迭代列表时删除元素。

  1. list_1 = [1, 2, 3, 4]
  2. list_2 = [1, 2, 3, 4]
  3. list_3 = [1, 2, 3, 4]
  4. list_4 = [1, 2, 3, 4]
  5. for idx, item in enumerate(list_1):
  6. del item
  7. for idx, item in enumerate(list_2):
  8. list_2.remove(item)
  9. for idx, item in enumerate(list_3[:]):
  10. list_3.remove(item)
  11. for idx, item in enumerate(list_4):
  12. list_4.pop(idx)

Output:

  1. >>> list_1
  2. [1, 2, 3, 4]
  3. >>> list_2
  4. [2, 4]
  5. >>> list_3
  6. []
  7. >>> list_4
  8. [2, 4]

说明:

在迭代时修改对象是一个很愚蠢的主意,正确的做法是迭代对象的副本,list_3[:]就是这么做的。

del、remove、pop 的不同:

  • del var_name 只是从本地或全局命名空间中删除了 var_name(这就是为什么 list_1 没有受到影响)。
  • remove 会删除第一个匹配到的指定值,而不是特定的索引,如果找不到值则抛出 ValueError 异常。
  • pop 则会删除指定索引处的元素并返回它,如果指定了无效的索引则抛出 IndexError 异常。

为什么输出是 [2, 4]?

列表迭代是按索引进行的,所以当我们从 list_2list_4 中删除 1 时,列表的内容就变成了[2, 3, 4]。剩余元素会依次位移,也就是说,2的索引会变为 0,3会变为 1。由于下一次迭代将获取索引为 1 的元素(即3), 因此2将被彻底的跳过。类似的情况会交替发生在列表中的每个元素上。

29. 循环变量泄漏!

  1. for x in range(7):
  2. if x == 6:
  3. print(x, ': for x inside loop')
  4. print(x, ': x in global')

Output:

  1. 6 : for x inside loop
  2. 6 : x in global

  1. # 这次我们先初始化x
  2. x = -1
  3. for x in range(7):
  4. if x == 6:
  5. print(x, ': for x inside loop')
  6. print(x, ': x in global')

Output:

  1. 6 : for x inside loop
  2. 6 : x in global

  1. x = 1
  2. print([x for x in range(5)])
  3. print(x, ': x in global')

Output(Python 2):

  1. [0, 1, 2, 3, 4]
  2. (4, ': x in global')

Output(Python 3):

  1. [0, 1, 2, 3, 4]
  2. 1 : x in global

说明:

Python 中,for 循环使用所在作用域并在结束后保留定义的循环变量。如果我们曾在全局命名空间中定义过循环变量,它会重新绑定现有变量。

Python 2.xPython 3.x 解释器在列表推导式示例中的输出差异,在文档 What’s New In Python 3.0 中可以找到相关的解释:

"列表推导不再支持句法形式[... for var in item1, item2, ...]。使用[... for var in (item1, item2, ...)]代替。另外注意,列表推导具有不同的语义:它们更接近于list()构造函数中生成器表达式的语法糖,特别是循环控制变量不再泄漏到周围的作用域中。"

简单来说,就是 python2 中,列表推导式依然存在循环控制变量泄露,而 python3 中不存在。

30. 当心默认的可变参数!

  1. def some_func(default_arg=[]):
  2. default_arg.append("some_string")
  3. return default_arg

Output:

  1. >>> some_func()
  2. ['some_string']
  3. >>> some_func()
  4. ['some_string', 'some_string']
  5. >>> some_func([])
  6. ['some_string']
  7. >>> some_func()
  8. ['some_string', 'some_string', 'some_string']

说明:

Python 中函数的默认可变参数并不是每次调用该函数时都会被初始化。相反,它们会使用最近分配的值作为默认值。当我们明确的将 [] 作为参数传递给 some_func 的时候,就不会使用 default_arg 的默认值, 所以函数会返回我们所期望的结果。

  1. >>> some_func.__defaults__ # 这里会显示函数的默认参数的值
  2. ([],)
  3. >>> some_func()
  4. >>> some_func.__defaults__
  5. (['some_string'],)
  6. >>> some_func()
  7. >>> some_func.__defaults__
  8. (['some_string', 'some_string'],)
  9. >>> some_func([])
  10. >>> some_func.__defaults__
  11. (['some_string', 'some_string'],)

避免可变参数导致的错误的常见做法是将 None 指定为参数的默认值,然后检查是否有值传给对应的参数。例:

  1. def some_func(default_arg=None):
  2. if not default_arg:
  3. default_arg = []
  4. default_arg.append("some_string")
  5. return default_arg

31. 捕获异常

这里讲的是 python2

  1. some_list = [1, 2, 3]
  2. try:
  3. # 这里会抛出异常 ``IndexError``
  4. print(some_list[4])
  5. except IndexError, ValueError:
  6. print("Caught!")
  7. try:
  8. # 这里会抛出异常 ``ValueError``
  9. some_list.remove(4)
  10. except IndexError, ValueError:
  11. print("Caught again!")

Output:

  1. Caught!
  2. ValueError: list.remove(x): x not in list

说明:

如果你想要同时捕获多个不同类型的异常时,你需要将它们用括号包成一个元组作为第一个参数传递。第二个参数是可选名称,如果你提供,它将与被捕获的异常实例绑定。

也就是说,代码原意是捕获 IndexError, ValueError 两种异常,但在 python2 中,必须写成(IndexError, ValueError),示例中的写法解析器会将 ValueError 理解成绑定的异常实例名。

python3 中,不会有这种误解,因为必须使用as关键字。

32. +=就地修改

  1. a = [1, 2, 3, 4]
  2. b = a
  3. a = a + [5, 6, 7, 8]

Output:

  1. >>> a
  2. [1, 2, 3, 4, 5, 6, 7, 8]
  3. >>> b
  4. [1, 2, 3, 4]

  1. a = [1, 2, 3, 4]
  2. b = a
  3. a += [5, 6, 7, 8]

Output:

  1. >>> a
  2. [1, 2, 3, 4, 5, 6, 7, 8]
  3. >>> b
  4. [1, 2, 3, 4, 5, 6, 7, 8]

说明:

a += b 并不总是与 a = a + b 表现相同。

表达式 a = a + [5,6,7,8] 会生成一个新列表,并让 a 引用这个新列表,同时保持 b 不变。

表达式 a += [5, 6, 7, 8] 实际上是使用的是 extend() 函数,就地修改列表,所以 ab 仍然指向已被修改的同一列表。

33. 外部作用域变量

  1. a = 1
  2. def some_func():
  3. return a
  4. def another_func():
  5. a += 1
  6. return a

Output:

  1. >>> some_func()
  2. 1
  3. >>> another_func()
  4. UnboundLocalError: local variable 'a' referenced before assignment

说明:

当在函数中引用外部作用域的变量时,如果不对这个变量进行修改,则可以直接引用,如果要对其进行修改,则必须使用 global 关键字,否则解析器将认为这个变量是局部变量,而做修改之前并没有定义它,所以会报错。

  1. def another_func()
  2. global a
  3. a += 1
  4. return a

Output:

  1. >>> another_func()
  2. 2

34. 小心链式操作

  1. >>> (False == False) in [False] # 可以理解
  2. False
  3. >>> False == (False in [False]) # 可以理解
  4. False
  5. >>> False == False in [False] # 为毛?
  6. True
  7. >>> True is False == False
  8. False
  9. >>> False is False is False
  10. True
  11. >>> 1 > 0 < 1
  12. True
  13. >>> (1 > 0) < 1
  14. False
  15. >>> 1 > (0 < 1)
  16. False

根据 https://docs.python.org/2/reference/expressions.html#not-in

形式上,如果 a, b, c, ..., y, z 是表达式,而 op1, op2, ..., opN 是比较运算符,那么 a op1 b op2 c ... y opN z 就等于 a op1 b and b op2 c and ... y opN z,除了每个表达式最多被评估一次。

  • False == False in [False] 就相当于 False == False and False in [False]
  • 1 > 0 < 1 就相当于 1 > 0 and 0 < 1

虽然上面的例子似乎很愚蠢,但是像 a == b == c0 <= x <= 100 就很棒了。

35. 忽略类作用域的名称解析

① 生成器表达式

  1. x = 5
  2. class SomeClass:
  3. x = 17
  4. y = (x for i in range(10))

Output:

  1. >>> list(SomeClass.y)
  2. [5, 5, 5, 5, 5, 5, 5, 5, 5, 5]

② 列表推导式

  1. x = 5
  2. class SomeClass:
  3. x = 17
  4. y = [x for i in range(10)]

Output(Python 2):

  1. >>> SomeClass.y
  2. [17, 17, 17, 17, 17, 17, 17, 17, 17, 17]

Output(Python 3):

  1. >>> SomeClass.y
  2. [5, 5, 5, 5, 5, 5, 5, 5, 5, 5]

说明:

  • 类定义中嵌套的作用域会忽略类内的名称绑定。
  • 生成器表达式有它自己的作用域。
  • Python 3 开始,列表推导式也有自己的作用域。

36. 元组

  1. x, y = (0, 1) if True else None, None

Output:

  1. >>> x, y # 期望的结果是 (0, 1)
  2. ((0, 1), None)

  1. t = ('one', 'two')
  2. for i in t:
  3. print(i)
  4. t = ('one')
  5. for i in t:
  6. print(i)
  7. t = ()
  8. print(t)

Output:

  1. one
  2. two
  3. o
  4. n
  5. e
  6. tuple()

说明:

  • 对于 1,正确的语句是 x, y = (0, 1) if True else (None, None)
  • 对于 2,正确的语句是 t = ('one',) 或者 t = 'one', (缺少逗号) 否则解释器会认为 t 是一个字符串,并逐个字符对其进行迭代。
  • () 是一个特殊的标记,表示空元组。

37. else

① 循环末尾的 else

  1. def does_exists_num(l, to_find):
  2. for num in l:
  3. if num == to_find:
  4. print("Exists!")
  5. break
  6. else:
  7. print("Does not exist")

Output:

  1. >>> some_list = [1, 2, 3, 4, 5]
  2. >>> does_exists_num(some_list, 4)
  3. Exists!
  4. >>> does_exists_num(some_list, -1)
  5. Does not exist

② try 末尾的 else

  1. try:
  2. pass
  3. except:
  4. print("Exception occurred!!!")
  5. else:
  6. print("Try block executed successfully...")

Output:

  1. Try block executed successfully...

说明:

循环后的 else 子句只会在循环执行完成(没有触发 break、return 语句)的情况下才会执行。

try 之后的 else 子句也被称为 "完成子句",因为在 try 语句中到达 else 子句意味着 try 块实际上已成功完成。

38. 名称改写

  1. class Yo(object):
  2. def __init__(self):
  3. self.__honey = True
  4. self.bitch = True

Output:

  1. >>> Yo().bitch
  2. True
  3. >>> Yo().__honey
  4. AttributeError: 'Yo' object has no attribute '__honey'
  5. >>> Yo()._Yo__honey
  6. True

说明:

python 中不能像 Java 那样使用 private 修饰符创建私有属性。但是,解释器会通过给类中以 __(双下划线)开头且结尾最多只有一个下划线的类成员名称加上 __类名_ 来修饰。这能避免子类意外覆盖父类的“私有”属性。

举个例子:有人编写了一个名为 Dog 的类,这个类的内部用到了 mood 实例属性,但是没有将其开放。现在,你创建了 Dog 类的子类 Beagle,如果你在毫不知情的情况下又创建了一个 mood 实例属性,那么在继承的方法中就会把 Dog 类的 mood 属性覆盖掉。

为了避免这种情况,python 会将 __mood 变成 _Dog__mood,而对于 Beagle 类来说,会变成 _Beagle__mood。这个语言特性就叫名称改写(name mangling)。

39. +=更快

  1. >>> timeit.timeit("s1 = s1 + s2 + s3", setup="s1 = ' ' * 100000; s2 = ' ' * 100000; s3 = ' ' * 100000", number=100)
  2. 0.25748300552368164
  3. # 用 "+=" 连接三个字符串:
  4. >>> timeit.timeit("s1 += s2 + s3", setup="s1 = ' ' * 100000; s2 = ' ' * 100000; s3 = ' ' * 100000", number=100)
  5. 0.012188911437988281

说明:

连接两个以上的字符串时 +=+ 更快,因为在计算过程中第一个字符串(例如, s1 += s2 + s3 中的 s1)不会被销毁。(就是 += 执行的是追加操作,少了一个销毁新建的动作。)

Python:鲜为人知的功能特性(下)的更多相关文章

  1. Python:鲜为人知的功能特性(上)

    GitHub 上有一个名为<What the f*ck Python!>的项目,这个有趣的项目意在收集 Python 中那些难以理解和反人类直觉的例子以及鲜为人知的功能特性,并尝试讨论这些 ...

  2. python中那些鲜为人知的功能特性

    经常逛GitHub的可能关注一个牛叉的项目,叫 What the f*ck Python! 这个项目列出了几乎所有python中那些鲜为人知的功能特性,有些功能第一次遇见时,你会冒出 what the ...

  3. python 里面的单下划线与双下划线的区别

    python 里面的单下划线与双下划线的区别 Python 用下划线作为变量前缀和后缀指定特殊变量. _xxx 不能用'from moduleimport *'导入 __xxx__ 系统定义名字 __ ...

  4. python中那些双下划线开头得函数和变量--转载

    Python中下划线---完全解读     Python 用下划线作为变量前缀和后缀指定特殊变量 _xxx 不能用'from module import *'导入 __xxx__ 系统定义名字 __x ...

  5. python基础——面向对象进阶下

    python基础--面向对象进阶下 1 __setitem__,__getitem,__delitem__ 把对象操作属性模拟成字典的格式 想对比__getattr__(), __setattr__( ...

  6. python python中那些双下划线开头的那些函数都是干啥用用的

    1.写在前面 今天遇到了__slots__,,所以我就想了解下python中那些双下划线开头的那些函数都是干啥用用的,翻到了下面这篇博客,看着很全面,我只了解其中的一部分,还不敢乱下定义. 其实如果足 ...

  7. Python里的单下划线,双下划线,以及前后都带下划线的意义

    Python里的单下划线,双下划线,以及前后都带下划线的意义: 单下划线如:_name 意思是:不能通过from modules import * 导入,如需导入需要:from modules imp ...

  8. python中那些双下划线开头得函数和变量

    Python中下划线---完全解读     Python 用下划线作为变量前缀和后缀指定特殊变量 _xxx 不能用’from module import *’导入 __xxx__ 系统定义名字 __x ...

  9. python获取指定目录下所有文件名os.walk和os.listdir

    python获取指定目录下所有文件名os.walk和os.listdir 觉得有用的话,欢迎一起讨论相互学习~Follow Me os.walk 返回指定路径下所有文件和子文件夹中所有文件列表 其中文 ...

随机推荐

  1. compare.go

    package clientv3 import (     pb "github.com/coreos/etcd/etcdserver/etcdserverpb" ) type C ...

  2. BZOJ_3307_雨天的尾巴_线段树合并+树上差分

    BZOJ_3307_雨天的尾巴_线段树合并 Description N个点,形成一个树状结构.有M次发放,每次选择两个点x,y 对于x到y的路径上(含x,y)每个点发一袋Z类型的物品.完成 所有发放后 ...

  3. BZOJ_2223_[Coci 2009]PATULJCI_主席树

    BZOJ_2223_[Coci 2009]PATULJCI_主席树 Description Input 10 3 1 2 1 2 1 2 3 2 3 3 8 1 2 1 3 1 4 1 5 2 5 2 ...

  4. Charpter3 名字 作用域 约束

    一个对象拥有其语义价值的区域<其作用域 当一个变量将不再被使用,那它应该被理想的回收机制回收.但现实是我们仅当一个变量离开了其作用域,或变成不可访问,才考虑回收. 然而,作用域规则有其优点:1. ...

  5. keras实现简单CNN人脸关键点检测

    用keras实现人脸关键点检测 改良版:http://www.cnblogs.com/ansang/p/8583122.html 第一步:准备好需要的库 tensorflow  1.4.0 h5py ...

  6. "元素隐式具有 “any” 类型,因为类型“Shared”没有索引签名"问题解决思路

    最近在构建一个typescript项目时如下代码在项目框架里vscode报错元素隐式具有 "any" 类型,因为类型“Shared”没有索引签名;很有意思的是当我们单独的把这段代码 ...

  7. TensorFlow从1到2(七)线性回归模型预测汽车油耗以及训练过程优化

    线性回归模型 "回归"这个词,既是Regression算法的名称,也代表了不同的计算结果.当然结果也是由算法决定的. 不同于前面讲过的多个分类算法或者逻辑回归,线性回归模型的结果是 ...

  8. EIGRP 高级实验

    一.环境准备 1. 软件:GNS3 2. 路由:c7200 二.实验操作 实验要求: 1.掌握EIGRP  的不等价均衡的条件. 2.掌握EIGRP  的metric  值修改方法. 3.掌握 EIG ...

  9. C# 跳转新的标签页

    ///这个是拿别人的,找到好多这个方法,溜了,不知道谁是原创 protected void btnPrint_Click(object sender, EventArgs e)        {    ...

  10. [PHP] 使用反射实现的控制反转

    搬家进程中反射实现控制反转,样做的好处是可以通过配置项动态的控制下面那个类的属性 1.$this->getObject($class, $config->getConfig('param' ...