BruceChen7 / gitblog

My blog
6 stars 1 forks source link

WTF Python #23

Open BruceChen7 opened 4 years ago

BruceChen7 commented 4 years ago

资料来源

is 和 ==

>>> a = 256
>>> b = 256
>>> a is b
True

>>> a = 257
>>> b = 257
>>> a is b
False

>>> a = []
>>> b = []
>>> a is b
False

>>> a = tuple()
>>> b = tuple()
>>> a is b
True

当启动python时候,会分配-5到256的对象。这是因为这些值使用的很频繁

The current implementation keeps an array of integer objects for all integers between -5 and 256, when you create an int in that range you just get back a reference to the existing object. So it should be possible to change the value of 1. I suspect the behavior of Python, in this case, is undefine

>>> id(256)
10922528
>>> a = 256
>>> b = 256
>>> id(a)
10922528
>>> id(b)
10922528
>>> id(257)
140084850247312
>>> x = 257
>>> y = 257
>>> id(x)
140084850247440
>>> id(y)
140084850247344

同样的优化适应于不可变的对象,比如空tuple,而list是可变的,所以is判断为假。

Both a and b refer to the same object when initialized with same value in the same line.

>>> a, b = 257, 257
>>> id(a)
140640774013296
>>> id(b)
140640774013296
>>> a = 257
>>> b = 257
>>> id(a)
140640774013392
>>> id(b)
140640774013488

hash

some_dict = {}
some_dict[5.5] = "JavaScript"
some_dict[5.0] = "Ruby"
some_dict[5] = "Python"
>> some_dict[5.5]
"JavaScript"
>>> some_dict[5.0] # "Python" destroyed the existence of "Ruby"?
"Python"
>>> some_dict[5] 
"Python"

>>> complex_five = 5 + 0j
>>> type(complex_five)
complex
>>> some_dict[complex_five]
"Python"

python中字典的相等性测试是by equivalence, not identity. 尽管5,5.0,5 + 0j都是不同对象,但是都相等

>>> 5 == 5.0 == 5 + 0j
True
>>> 5 is not 5.0 is not 5 + 0j
True
>>> some_dict = {}
>>> some_dict[5.0] = "Ruby"
>>> 5.0 in some_dict
True
>>> (5 in some_dict) and (5 + 0j in some_dict)
True

内心深处,我们都一样

class WTF:
  pass
>>> WTF() == WTF() # two different instances can't be equal
False
>>> WTF() is WTF() # identities are also different
False
>>> hash(WTF()) == hash(WTF()) # hashes _should_ be different as well
True
>>> id(WTF()) == id(WTF())
True

未完待续

keep trying..

def some_func():
    try:
        return 'from_try'
    finally:
        return 'from_finally'

def another_func(): 
    for _ in range(3):
        try:
            continue
        finally:
            print("Finally!")

def one_more_func(): # A gotcha!
    try:
        for i in range(3):
            try:
                1 / i
            except ZeroDivisionError:
                # Let's throw it here and handle it outside for loop
                raise ZeroDivisionError("A trivial divide by zero error")
            finally:
                print("Iteration", i)
                break
    except ZeroDivisionError as e:
        print("Zero division error occurred", e)

>>> some_func()
'from_finally'

>>> another_func()
Finally!
Finally!
Finally!

>>> 1 / 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero

>>> one_more_func()
Iteration 0

for

some_string = "wtf"
some_dict = {}
for i, some_dict[i] in enumerate(some_string):
    i = 10
>>> some_dict # An indexed dict appears.
{0: 'w', 1: 't', 2: 'f'}

和这个类比:

for i in range(4):
    print(i)
    i = 10

for 语句的定义:

for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]

{exprlist} = {next_value} 在每次迭代的时候都会呗执行。 上面的代码执行:

>>> i, some_dict[i] = (0, 'w')
>>> i, some_dict[i] = (1, 't')
>>> i, some_dict[i] = (2, 'f')
>>> some_dict

list 坑

# Let's initialize a row
row = [""] * 3 #row i['', '', '']
# Let's make a board
board = [row] * 3

>>> board
[['', '', ''], ['', '', ''], ['', '', '']]
>>> board[0]
['', '', '']
>>> board[0][0]
''
>>> board[0][0] = "X"
>>> board
[['X', '', ''], ['X', '', ''], ['X', '', '']]

我们可以这样避免:

>>> board = [['']*3 for _ in range(3)]
>>> board[0][0] = "X"
>>> board
[['X', '', ''], ['', '', ''], ['', '', '']]

output function

funcs = []
results = []
for x in range(7):
    def some_func():
        return x
    funcs.append(some_func)
    results.append(some_func())  # note the function call here
funcs_results = [func() for func in funcs]

>>> results
[0, 1, 2, 3, 4, 5, 6]
>>> funcs_results
[6, 6, 6, 6, 6, 6, 6]

>>> powers_of_x = [lambda x: x**i for i in range(10)]
>>> [f(2) for f in powers_of_x]
[512, 512, 512, 512, 512, 512, 512, 512, 512, 512]
funcs = []
for x in range(7):
    def some_func(x=x):
        return x
    funcs.append(some_func)
>>> funcs_results = [func() for func in funcs]
>>> funcs_results
[0, 1, 2, 3, 4, 5, 6]

list删除

list_1 = [1, 2, 3, 4]
list_2 = [1, 2, 3, 4]
list_3 = [1, 2, 3, 4]
list_4 = [1, 2, 3, 4]

for idx, item in enumerate(list_1):
    del item

for idx, item in enumerate(list_2):
    list_2.remove(item)

for idx, item in enumerate(list_3[:]):
    list_3.remove(item)

for idx, item in enumerate(list_4):
    list_4.pop(idx)

>>> list_1
[1, 2, 3, 4]
>>> list_2
[2, 4]
>>> list_3
[]
>>> list_4
[2, 4]

Why the output is [2, 4]?

Loop variables leaking out!

for x in range(7):
    if x == 6:
        print(x, ': for x inside loop')
print(x, ': x in global')

6 : for x inside loop
6 : x in global

# This time let's initialize x first
x = -1
for x in range(7):
    if x == 6:
        print(x, ': for x inside loop')
print(x, ': x in global')

# python 2
>>> x = 1
>>> print([x for x in range(5)])
[0, 1, 2, 3, 4]
>>> print(x)
4

# python3
>>> x = 1
>>> print([x for x in range(5)])
[0, 1, 2, 3, 4]
>>> print(x)
1

Beware of default mutable arguments!

def some_func(default_arg=[]):
    default_arg.append("some_string")
    return default_arg
>>> some_func()
['some_string']
>>> some_func()
['some_string', 'some_string']
>>> some_func([])
['some_string']
>>> some_func()
['some_string', 'some_string', 'some_string']

>> some_func.__defaults__ #This will show the default argument values for the function
([],)
>>> some_func()
>>> some_func.__defaults__
(['some_string'],)
>>> some_func()
>>> some_func.__defaults__
(['some_string', 'some_string'],)
>>> some_func([])
>>> some_func.__defaults__
(['some_string', 'some_string'],)

使用None来避免

def some_func(default_arg=None):
    if default_arg is None:
        default_arg = []
    default_arg.append("some_string")
    return default_arg

Catching the Exceptions

some_list = [1, 2, 3]
try:
    # This should raise an ``IndexError``
    print(some_list[4])
except IndexError, ValueError:
    print("Caught!")

try:
    # This should raise a ``ValueError``
    some_list.remove(4)
except IndexError, ValueError:
    print("Caught again!")

# output 2.x
Caught!

ValueError: list.remove(x): x not in list

# output 3.x
  File "<input>", line 3
    except IndexError, ValueError:
                     ^
SyntaxError: invalid syntax

对于python2

some_list = [1, 2, 3]
try:
   # This should raise a ``ValueError``
   some_list.remove(4)
except (IndexError, ValueError), e:
   print("Caught again!")
   print(e)

对于python3

some_list = [1, 2, 3]
try:
    some_list.remove(4)
except (IndexError, ValueError) as e:
    print("Caught again!")
    print(e)

Wild imports *

# File: module.py

def some_weird_name_func_():
    print("works!")

def _another_weird_name_func():
    print("works!")

>>> from module import *
>>> some_weird_name_func_()
"works!"
>>> _another_weird_name_func()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name '_another_weird_name_func' is not defined
>>> from module import some_weird_name_func_, _another_weird_name_func
>>> _another_weird_name_func()
works!
__all__ = ['_another_weird_name_func']

def some_weird_name_func_():
    print("works!")

def _another_weird_name_func():
    print("works!")

>>> _another_weird_name_func()
"works!"
>>> some_weird_name_func_()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'some_weird_name_func_' is not defined