逆向之旅:七個(gè)讓Python編程更糟糕的小技
文章帶大家進(jìn)入Python編程的奇特角落,探索那些完全出乎意料甚至無(wú)厘頭的編程事實(shí)。在這里,你可以窺見(jiàn)如何在一行內(nèi)完成復(fù)雜的函數(shù),或者是如何完全無(wú)視掉代碼中的assert語(yǔ)句。
如果你想在Python編程方面變得更糟糕,那么,這篇文章就是為你量身定制的。
(1) _.__.___.____._____ 也能行
class Test:
def __getattr__(self, key):
return Test()
在一個(gè)類中,當(dāng)1用于訪問(wèn)someattribute屬性2時(shí)調(diào)用magic方法someattributes屬性沒(méi)有顯式定義__getattribute__object.someattribute。
class Test:
def __getattr__(self, key):
return Test()
_ = Test()
print(_)
print(_.__)
print(_.__.___)
print(_.__.___.____)
print(_.__.___.____._____)
# <__main__.Test object at 0x1098834d0>
# <__main__.Test object at 0x109883500>
# <__main__.Test object at 0x1098835c0>
# <__main__.Test object at 0x1098835c0>
# <__main__.Test object at 0x109883500>
- _就是一個(gè)測(cè)試類對(duì)象
- _.__調(diào)用,返回另一個(gè) Test 對(duì)象__getattr__
- _.__.___又一次調(diào)用,返回另一個(gè)Test 對(duì)象__getattr__
- _.__.___.____再一次調(diào)用,返回另一個(gè)Test 對(duì)象__getattr__
- _.__.___.____._____繼續(xù)調(diào)用,返回另一個(gè)Test 對(duì)象__getattr__
- 這就是可能的_.__.___.____._____
(2) 利用exec()在一行中寫(xiě)代碼
如果你曾經(jīng)想要學(xué)的是如何在一行代碼中寫(xiě)出任何 Python 函數(shù),那么這點(diǎn)就適合你。假設(shè)有一個(gè)多行函數(shù),但是并不知道如何才能合法地把它壓縮成一行代碼
def hi():
print('apple')
print('orange')
為了便于理解,這里有一個(gè)簡(jiǎn)明易懂的例子。
exec("def hi():\n. print('apple')\n print('orange')")
exec()會(huì)將字符串作為Python代碼來(lái)執(zhí)行 因此,如果將函數(shù)編成字符串格式并把提供給exec,就成功地用python的一行代碼來(lái)寫(xiě)出這個(gè)函數(shù)了
(3) Dog()()()()可以是有效代碼
class Dog:
pass
dog = Dog()
print(dog()) # error
目前得到了一個(gè)錯(cuò)誤,這是因?yàn)槟J(rèn)情況下,是不能將一個(gè)對(duì)象像一個(gè)函數(shù)一樣去調(diào)用的。
class Dog:
def __call__(self):
return 'apple pie'
dog = Dog()
print(dog()) # apple pie
定義魔術(shù)方法__call__可以決定對(duì)象的調(diào)用行為,就像對(duì)函數(shù)進(jìn)行調(diào)用一樣。
class Dog:
def __call__(self):
return Dog()
dog = Dog()
print(dog()) # <class '__main__.Dog'>
在這,讓__call__返回一個(gè)新的 Dog 對(duì)象。
print(Dog()()()()())
由于每次都得到一個(gè)新的 Dog 對(duì)象,無(wú)論何時(shí)對(duì)Dog對(duì)象進(jìn)行調(diào)用,都可以無(wú)限地調(diào)用下去。
(4) 多個(gè)負(fù)號(hào)的問(wèn)題
x = -1--2---3----4-----5
這是有效的 Python 代碼:
- 1-2可以被解讀為 1 減 2
- -1則被看成負(fù)數(shù)(不是減法的負(fù)號(hào))
- --1是負(fù)負(fù),就等于 1
- ---1是負(fù)負(fù)負(fù)的,又變成了 -1
- 這種情況就會(huì)一直持續(xù)下去
-1--2---3----4-----5:因此,這是合法的 Python 代碼-1-(-2)-(--3)-(---4)-(----5)
(5)如何限制使用print()
print = None
print('hello world')
# TypeError: 'NoneType' object is not callable
在這,將print = None,將None賦值給了print。這覆蓋了非常熟悉的常用函數(shù),因此我們現(xiàn)在無(wú)法使用print()。
print = None
__builtins__.print('hello') # hello
但可以用__builtins__.print來(lái)訪問(wèn)原來(lái)的print()功能!
print = None
__builtins__ = None
如果讓__builtins__設(shè)置為None?,F(xiàn)在沒(méi)有人能再使用 print() 了。
(6)讓全局變量都消失
a = 4
b = 5
c = 6
keys = list(globals().keys())
for key in keys:
del globals()[key]
del globals()['keys']
del globals()['key']
print(globals()) # {}
print(a) # 'a' 沒(méi)有定義
- globals()會(huì)返回含有所有全局變量的字典
- 可以刪掉所有在globals()中的鍵值對(duì)
- 刪完后,globals()變成了空的
- 這意味著沒(méi)有剩余的全局變量,并且之前定義的所有全局變量都一去不復(fù)返了
(7)可無(wú)視assert語(yǔ)句
你是不是經(jīng)常assert代碼語(yǔ)句困擾?別擔(dān)心,因?yàn)橛幸环N方法能讓你完全無(wú)視掉assert語(yǔ)句。
# hello.py
assert 1 == 2
如果用python hello.py來(lái)運(yùn)行它,由于 assert 語(yǔ)句,會(huì)收到一個(gè) AssertionError。
# hello.py
assert 1 == 2
- 也可以選擇用python -O hello.py來(lái)運(yùn)行它
- -O標(biāo)志讓 Python 忽略所有的assert語(yǔ)句
- 現(xiàn)在,可以讓你的代碼忽略所有 assert 語(yǔ)句,無(wú)論它們會(huì)捕獲何種錯(cuò)誤!畢竟,誰(shuí)在乎呢?
結(jié)論
文章帶大家進(jìn)入Python編程的奇特角落,探索那些完全出乎意料甚至無(wú)厘頭的編程事實(shí)。在這里,你可以窺見(jiàn)如何在一行內(nèi)完成復(fù)雜的函數(shù),或者是如何完全無(wú)視掉代碼中的assert語(yǔ)句。
如果你對(duì)編程充滿好奇或者想成為一個(gè)更糟糕的程序員,那么這篇文章將為你揭開(kāi)一個(gè)被藏在細(xì)節(jié)里的驚人世界。