知识补充
tracemalloc
标准库tracemalloc,可以统计内存使用情况
import tracemalloc # from 3.4
tracemalloc.start() # 开始跟踪内存分配
d = [dict(zip('xy', (5, 6))) for i in range(1000000)] # 237M
t = [tuple(zip('xy', (5, 6))) for i in range(1000000)] # 191M
snapshot = tracemalloc.take_snapshot() # 快照,当前内存分配
top_stats = snapshot.statistics('lineno') # 快照对象的统计
for stat in top_stats:
print(stat)
<ipython-input-3-536811a55983>:5: size=237 MiB, count=1999978, average=124 B
<ipython-input-3-536811a55983>:6: size=191 MiB, count=2998207, average=67 B
d:\miniconda3\lib\linecache.py:137: size=328 KiB, count=3161, average=106 B
d:\miniconda3\lib\selectors.py:314: size=144 KiB, count=3, average=48.0 KiB
<ipython-input-2-ff42f2538adf>:6: size=125 KiB, count=1999, average=64 B
<ipython-input-1-ff42f2538adf>:6: size=125 KiB, count=1999, average=64 B
d:\miniconda3\lib\ntpath.py:545: size=68.9 KiB, count=676, average=104 B
d:\miniconda3\lib\ntpath.py:53: size=66.1 KiB, count=676, average=100 B
d:\miniconda3\lib\inspect.py:742: size=18.0 KiB, count=1, average=18.0 KiB
d:\miniconda3\lib\inspect.py:738: size=18.0 KiB, count=1, average=18.0 KiB
d:\miniconda3\lib\tracemalloc.py:469: size=13.9 KiB, count=216, average=66 B
d:\miniconda3\lib\tracemalloc.py:462: size=10.5 KiB, count=216, average=50 B
d:\miniconda3\lib\site-packages\IPython\core\compilerop.py:101: size=5957 B, count=138, average=43 B
d:\miniconda3\lib\codeop.py:133: size=5004 B, count=56, average=89 B
d:\miniconda3\lib\json\decoder.py:355: size=2927 B, count=20, average=146 B
d:\miniconda3\lib\site-packages\IPython\core\async_helpers.py:161: size=2569 B, count=14, average=184 B
d:\miniconda3\lib\site-packages\IPython\core\compilerop.py:133: size=2130 B, count=18, average=118 B
d:\miniconda3\lib\tracemalloc.py:498: size=2104 B, count=3, average=701 B
d:\miniconda3\lib\site-packages\tornado\gen.py:714: size=2088 B, count=3, average=696 B
d:\miniconda3\lib\sre_parse.py:416: size=1936 B, count=2, average=968 B
d:\miniconda3\lib\site-packages\IPython\core\history.py:764: size=1778 B, count=7, average=254 B
d:\miniconda3\lib\tracemalloc.py:466: size=1652 B, count=59, average=28 B
d:\miniconda3\lib\site-packages\IPython\core\history.py:709: size=1490 B, count=2, average=745 B
d:\miniconda3\lib\genericpath.py:19: size=1468 B, count=24, average=61 B
d:\miniconda3\lib\abc.py:228: size=1464 B, count=3, average=488 B
d:\miniconda3\lib\site-packages\traitlets\traitlets.py:600: size=1400 B, count=20, average=70 B
d:\miniconda3\lib\site-packages\IPython\core\interactiveshell.py:3249: size=1368 B, count=3, average=456 B
d:\miniconda3\lib\tracemalloc.py:499: size=1320 B, count=3, average=440 B
d:\miniconda3\lib\site-packages\ipykernel\jsonutil.py:191: size=1152 B, count=2, average=576 B
d:\miniconda3\lib\site-packages\ipykernel\jsonutil.py:177: size=1152 B, count=2, average=576 B
d:\miniconda3\lib\site-packages\IPython\core\ultratb.py:831: size=1112 B, count=1, average=1112 B
d:\miniconda3\lib\site-packages\zmq\sugar\socket.py:478: size=1104 B, count=6, average=184 B
d:\miniconda3\lib\_weakrefset.py:37: size=1056 B, count=8, average=132 B
d:\miniconda3\lib\traceback.py:68: size=1016 B, count=2, average=508 B
d:\miniconda3\lib\site-packages\IPython\core\ultratb.py:353: size=1016 B, count=2, average=508 B
d:\miniconda3\lib\site-packages\traitlets\config\configurable.py:121: size=992 B, count=2, average=496 B
d:\miniconda3\lib\site-packages\jupyter_client\session.py:911: size=990 B, count=6, average=165 B
d:\miniconda3\lib\site-packages\tornado\ioloop.py:602: size=920 B, count=2, average=460 B
d:\miniconda3\lib\_weakrefset.py:48: size=896 B, count=4, average=224 B
d:\miniconda3\lib\_weakrefset.py:84: size=880 B, count=11, average=80 B
d:\miniconda3\lib\_weakrefset.py:38: size=864 B, count=8, average=108 B
d:\miniconda3\lib\site-packages\traitlets\traitlets.py:1155: size=831 B, count=12, average=69 B
d:\miniconda3\lib\site-packages\ipykernel\kernelbase.py:542: size=784 B, count=2, average=392 B
d:\miniconda3\lib\site-packages\ipykernel\kernelbase.py:365: size=784 B, count=2, average=392 B
d:\miniconda3\lib\site-packages\ipykernel\kernelbase.py:272: size=784 B, count=2, average=392 B
d:\miniconda3\lib\site-packages\IPython\core\interactiveshell.py:3102: size=746 B, count=1, average=746 B
d:\miniconda3\lib\site-packages\IPython\core\interactiveshell.py:3343: size=696 B, count=1, average=696 B
d:\miniconda3\lib\site-packages\IPython\core\ultratb.py:408: size=688 B, count=1, average=688 B
d:\miniconda3\lib\tracemalloc.py:497: size=680 B, count=1, average=680 B
d:\miniconda3\lib\site-packages\IPython\utils\terminal.py:110: size=672 B, count=1, average=672 B
d:\miniconda3\lib\sre_compile.py:187: size=664 B, count=1, average=664 B
d:\miniconda3\lib\sre_compile.py:146: size=664 B, count=1, average=664 B
d:\miniconda3\lib\sre_compile.py:126: size=664 B, count=1, average=664 B
d:\miniconda3\lib\site-packages\IPython\core\ultratb.py:319: size=664 B, count=1, average=664 B
d:\miniconda3\lib\site-packages\ipykernel\zmqshell.py:546: size=632 B, count=1, average=632 B
d:\miniconda3\lib\site-packages\IPython\core\ultratb.py:1139: size=632 B, count=1, average=632 B
d:\miniconda3\lib\threading.py:551: size=608 B, count=1, average=608 B
d:\miniconda3\lib\tracemalloc.py:387: size=600 B, count=4, average=150 B
d:\miniconda3\lib\site-packages\IPython\core\ultratb.py:1288: size=592 B, count=1, average=592 B
d:\miniconda3\lib\site-packages\IPython\core\interactiveshell.py:3326: size=584 B, count=3, average=195 B
d:\miniconda3\lib\inspect.py:1483: size=584 B, count=1, average=584 B
d:\miniconda3\lib\asyncio\base_events.py:1396: size=584 B, count=1, average=584 B
d:\miniconda3\lib\site-packages\ipykernel\zmqshell.py:556: size=576 B, count=1, average=576 B
d:\miniconda3\lib\site-packages\IPython\core\ultratb.py:1014: size=576 B, count=1, average=576 B
d:\miniconda3\lib\asyncio\events.py:145: size=576 B, count=1, average=576 B
d:\miniconda3\lib\site-packages\IPython\core\ultratb.py:1076: size=568 B, count=1, average=568 B
d:\miniconda3\lib\site-packages\IPython\utils\PyColorize.py:331: size=560 B, count=1, average=560 B
d:\miniconda3\lib\site-packages\IPython\core\ultratb.py:1086: size=560 B, count=1, average=560 B
d:\miniconda3\lib\sre_compile.py:579: size=544 B, count=1, average=544 B
d:\miniconda3\lib\inspect.py:1445: size=544 B, count=1, average=544 B
d:\miniconda3\lib\site-packages\IPython\core\ultratb.py:1101: size=536 B, count=1, average=536 B
d:\miniconda3\lib\site-packages\IPython\core\ultratb.py:1385: size=528 B, count=1, average=528 B
d:\miniconda3\lib\site-packages\IPython\core\ultratb.py:1082: size=528 B, count=1, average=528 B
d:\miniconda3\lib\site-packages\IPython\core\ultratb.py:1077: size=528 B, count=1, average=528 B
d:\miniconda3\lib\site-packages\IPython\core\ultratb.py:453: size=528 B, count=1, average=528 B
d:\miniconda3\lib\site-packages\IPython\core\interactiveshell.py:2045: size=528 B, count=1, average=528 B
d:\miniconda3\lib\site-packages\zmq\sugar\socket.py:475: size=520 B, count=3, average=173 B
d:\miniconda3\lib\sre_parse.py:197: size=520 B, count=1, average=520 B
d:\miniconda3\lib\inspect.py:1018: size=520 B, count=1, average=520 B
d:\miniconda3\lib\asyncio\base_events.py:544: size=512 B, count=2, average=256 B
d:\miniconda3\lib\site-packages\jupyter_client\session.py:863: size=512 B, count=1, average=512 B
d:\miniconda3\lib\site-packages\IPython\core\interactiveshell.py:3070: size=512 B, count=1, average=512 B
d:\miniconda3\lib\site-packages\IPython\core\interactiveshell.py:2043: size=512 B, count=1, average=512 B
d:\miniconda3\lib\tracemalloc.py:524: size=504 B, count=2, average=252 B
d:\miniconda3\lib\site-packages\ipykernel\iostream.py:351: size=504 B, count=1, average=504 B
d:\miniconda3\lib\site-packages\IPython\core\ultratb.py:1084: size=496 B, count=1, average=496 B
d:\miniconda3\lib\inspect.py:1183: size=496 B, count=1, average=496 B
d:\miniconda3\lib\typing.py:1154: size=488 B, count=1, average=488 B
d:\miniconda3\lib\traceback.py:352: size=488 B, count=1, average=488 B
d:\miniconda3\lib\traceback.py:345: size=488 B, count=1, average=488 B
d:\miniconda3\lib\asyncio\base_events.py:558: size=488 B, count=1, average=488 B
d:\miniconda3\lib\site-packages\IPython\core\ultratb.py:878: size=480 B, count=1, average=480 B
d:\miniconda3\lib\inspect.py:1182: size=480 B, count=1, average=480 B
d:\miniconda3\lib\inspect.py:1019: size=480 B, count=1, average=480 B
<ipython-input-1-ff42f2538adf>:9: size=480 B, count=1, average=480 B
d:\miniconda3\lib\tokenize.py:512: size=472 B, count=1, average=472 B
d:\miniconda3\lib\site-packages\tornado\platform\asyncio.py:164: size=472 B, count=1, average=472 B
d:\miniconda3\lib\site-packages\tornado\gen.py:641: size=472 B, count=1, average=472 B
d:\miniconda3\lib\site-packages\IPython\core\interactiveshell.py:2018: size=472 B, count=1, average=472 B
d:\miniconda3\lib\traceback.py:282: size=464 B, count=1, average=464 B
d:\miniconda3\lib\site-packages\IPython\utils\data.py:23: size=464 B, count=1, average=464 B
d:\miniconda3\lib\site-packages\IPython\core\ultratb.py:1148: size=464 B, count=1, average=464 B
d:\miniconda3\lib\tokenize.py:400: size=456 B, count=1, average=456 B
d:\miniconda3\lib\site-packages\IPython\core\ultratb.py:1129: size=456 B, count=1, average=456 B
d:\miniconda3\lib\site-packages\IPython\core\ultratb.py:974: size=456 B, count=1, average=456 B
d:\miniconda3\lib\site-packages\IPython\core\ultratb.py:877: size=456 B, count=1, average=456 B
d:\miniconda3\lib\site-packages\IPython\core\ultratb.py:367: size=456 B, count=1, average=456 B
d:\miniconda3\lib\site-packages\tornado\gen.py:142: size=448 B, count=4, average=112 B
d:\miniconda3\lib\site-packages\IPython\core\ultratb.py:1030: size=448 B, count=1, average=448 B
d:\miniconda3\lib\site-packages\IPython\core\ultratb.py:976: size=448 B, count=1, average=448 B
d:\miniconda3\lib\site-packages\IPython\core\hooks.py:152: size=448 B, count=1, average=448 B
<ipython-input-1-ff42f2538adf>:8: size=448 B, count=1, average=448 B
d:\miniconda3\lib\traceback.py:358: size=440 B, count=1, average=440 B
d:\miniconda3\lib\site-packages\IPython\core\ultratb.py:415: size=440 B, count=1, average=440 B
d:\miniconda3\lib\site-packages\IPython\core\getipython.py:23: size=440 B, count=1, average=440 B
d:\miniconda3\lib\tracemalloc.py:465: size=432 B, count=1, average=432 B
d:\miniconda3\lib\traceback.py:354: size=432 B, count=1, average=432 B
d:\miniconda3\lib\site-packages\tornado\queues.py:220: size=432 B, count=1, average=432 B
d:\miniconda3\lib\site-packages\ipykernel\ipkernel.py:342: size=432 B, count=1, average=432 B
d:\miniconda3\lib\site-packages\ipykernel\ipkernel.py:303: size=432 B, count=1, average=432 B
d:\miniconda3\lib\site-packages\ipykernel\ipkernel.py:296: size=432 B, count=1, average=432 B
d:\miniconda3\lib\site-packages\IPython\core\ultratb.py:1090: size=432 B, count=1, average=432 B
d:\miniconda3\lib\site-packages\tornado\queues.py:181: size=424 B, count=1, average=424 B
d:\miniconda3\lib\site-packages\ipykernel\ipkernel.py:339: size=424 B, count=1, average=424 B
d:\miniconda3\lib\site-packages\tornado\gen.py:191: size=400 B, count=5, average=80 B
d:\miniconda3\lib\site-packages\tornado\ioloop.py:690: size=352 B, count=3, average=117 B
d:\miniconda3\lib\site-packages\zmq\utils\jsonapi.py:40: size=336 B, count=2, average=168 B
d:\miniconda3\lib\site-packages\zmq\sugar\socket.py:400: size=336 B, count=2, average=168 B
d:\miniconda3\lib\site-packages\zmq\sugar\poll.py:99: size=336 B, count=2, average=168 B
d:\miniconda3\lib\site-packages\traitlets\traitlets.py:1139: size=336 B, count=2, average=168 B
d:\miniconda3\lib\site-packages\jupyter_client\session.py:621: size=336 B, count=2, average=168 B
d:\miniconda3\lib\site-packages\ipykernel\iostream.py:222: size=336 B, count=2, average=168 B
d:\miniconda3\lib\site-packages\ipykernel\iostream.py:214: size=336 B, count=2, average=168 B
d:\miniconda3\lib\site-packages\IPython\core\interactiveshell.py:117: size=312 B, count=5, average=62 B
d:\miniconda3\lib\site-packages\ipykernel\iostream.py:436: size=304 B, count=4, average=76 B
d:\miniconda3\lib\site-packages\IPython\utils\ipstruct.py:147: size=296 B, count=5, average=59 B
d:\miniconda3\lib\site-packages\jupyter_client\session.py:933: size=296 B, count=1, average=296 B
d:\miniconda3\lib\site-packages\jupyter_client\adapter.py:389: size=296 B, count=1, average=296 B
d:\miniconda3\lib\site-packages\IPython\core\compilerop.py:134: size=296 B, count=1, average=296 B
d:\miniconda3\lib\site-packages\tornado\gen.py:789: size=288 B, count=6, average=48 B
d:\miniconda3\lib\site-packages\tornado\gen.py:784: size=272 B, count=2, average=136 B
d:\miniconda3\lib\site-packages\IPython\core\interactiveshell.py:3243: size=264 B, count=5, average=53 B
d:\miniconda3\lib\site-packages\IPython\core\interactiveshell.py:2868: size=232 B, count=4, average=58 B
d:\miniconda3\lib\abc.py:204: size=224 B, count=4, average=56 B
d:\miniconda3\lib\contextlib.py:60: size=216 B, count=3, average=72 B
d:\miniconda3\lib\tracemalloc.py:467: size=196 B, count=7, average=28 B
d:\miniconda3\lib\site-packages\traitlets\traitlets.py:556: size=192 B, count=3, average=64 B
d:\miniconda3\lib\inspect.py:732: size=192 B, count=3, average=64 B
d:\miniconda3\lib\asyncio\base_events.py:596: size=192 B, count=2, average=96 B
d:\miniconda3\lib\contextlib.py:88: size=168 B, count=3, average=56 B
d:\miniconda3\lib\site-packages\tornado\gen.py:226: size=168 B, count=2, average=84 B
d:\miniconda3\lib\site-packages\jupyter_client\session.py:578: size=168 B, count=1, average=168 B
d:\miniconda3\lib\site-packages\IPython\core\interactiveshell.py:3246: size=168 B, count=1, average=168 B
d:\miniconda3\lib\json\encoder.py:215: size=168 B, count=1, average=168 B
<d:\miniconda3\lib\site-packages\decorator.py:decorator-gen-23>:2: size=168 B, count=1, average=168 B
d:\miniconda3\lib\site-packages\IPython\core\compilerop.py:63: size=158 B, count=2, average=79 B
d:\miniconda3\lib\sre_parse.py:771: size=144 B, count=2, average=72 B
d:\miniconda3\lib\site-packages\zmq\eventloop\zmqstream.py:271: size=144 B, count=2, average=72 B
d:\miniconda3\lib\site-packages\ipykernel\ipkernel.py:316: size=144 B, count=2, average=72 B
d:\miniconda3\lib\contextlib.py:63: size=144 B, count=2, average=72 B
<ipython-input-2-ff42f2538adf>:11: size=144 B, count=2, average=72 B
d:\miniconda3\lib\site-packages\IPython\core\interactiveshell.py:3058: size=136 B, count=2, average=68 B
d:\miniconda3\lib\site-packages\ipykernel\kernelbase.py:405: size=136 B, count=1, average=136 B
d:\miniconda3\lib\site-packages\IPython\core\interactiveshell.py:3230: size=136 B, count=1, average=136 B
d:\miniconda3\lib\site-packages\IPython\core\interactiveshell.py:2956: size=136 B, count=1, average=136 B
d:\miniconda3\lib\site-packages\IPython\core\ultratb.py:372: size=128 B, count=2, average=64 B
d:\miniconda3\lib\site-packages\IPython\core\interactiveshell.py:2943: size=112 B, count=2, average=56 B
d:\miniconda3\lib\site-packages\IPython\core\interactiveshell.py:2942: size=112 B, count=2, average=56 B
d:\miniconda3\lib\site-packages\tornado\queues.py:246: size=112 B, count=1, average=112 B
d:\miniconda3\lib\site-packages\jupyter_client\session.py:849: size=107 B, count=2, average=54 B
d:\miniconda3\lib\threading.py:288: size=104 B, count=2, average=52 B
d:\miniconda3\lib\site-packages\IPython\core\history.py:732: size=104 B, count=2, average=52 B
d:\miniconda3\lib\site-packages\jupyter_client\session.py:171: size=96 B, count=2, average=48 B
d:\miniconda3\lib\site-packages\IPython\core\interactiveshell.py:326: size=80 B, count=2, average=40 B
d:\miniconda3\lib\site-packages\IPython\core\interactiveshell.py:301: size=80 B, count=2, average=40 B
d:\miniconda3\lib\site-packages\tornado\platform\asyncio.py:176: size=80 B, count=1, average=80 B
d:\miniconda3\lib\site-packages\tornado\gen.py:706: size=80 B, count=1, average=80 B
d:\miniconda3\lib\sre_parse.py:112: size=72 B, count=1, average=72 B
d:\miniconda3\lib\site-packages\zmq\sugar\socket.py:479: size=72 B, count=1, average=72 B
d:\miniconda3\lib\site-packages\traitlets\traitlets.py:1421: size=72 B, count=1, average=72 B
d:\miniconda3\lib\shutil.py:1081: size=72 B, count=1, average=72 B
<ipython-input-2-ff42f2538adf>:5: size=72 B, count=1, average=72 B
<ipython-input-1-ff42f2538adf>:5: size=72 B, count=1, average=72 B
d:\miniconda3\lib\sre_parse.py:455: size=64 B, count=1, average=64 B
d:\miniconda3\lib\sre_parse.py:255: size=64 B, count=1, average=64 B
d:\miniconda3\lib\sre_parse.py:184: size=64 B, count=1, average=64 B
d:\miniconda3\lib\sre_compile.py:545: size=64 B, count=1, average=64 B
d:\miniconda3\lib\sre_compile.py:416: size=64 B, count=1, average=64 B
d:\miniconda3\lib\site-packages\IPython\core\interactiveshell.py:3236: size=64 B, count=1, average=64 B
d:\miniconda3\lib\site-packages\IPython\core\compilerop.py:132: size=56 B, count=2, average=28 B
d:\miniconda3\lib\tracemalloc.py:279: size=56 B, count=1, average=56 B
d:\miniconda3\lib\site-packages\tornado\gen.py:225: size=56 B, count=1, average=56 B
d:\miniconda3\lib\site-packages\IPython\core\ultratb.py:1028: size=56 B, count=1, average=56 B
d:\miniconda3\lib\site-packages\IPython\core\interactiveshell.py:3241: size=56 B, count=1, average=56 B
d:\miniconda3\lib\functools.py:778: size=56 B, count=1, average=56 B
d:\miniconda3\lib\enum.py:291: size=56 B, count=1, average=56 B
d:\miniconda3\lib\site-packages\zmq\sugar\attrsettr.py:40: size=55 B, count=1, average=55 B
d:\miniconda3\lib\site-packages\tornado\gen.py:764: size=48 B, count=1, average=48 B
d:\miniconda3\lib\site-packages\tornado\gen.py:712: size=48 B, count=1, average=48 B
d:\miniconda3\lib\site-packages\ipykernel\kernelbase.py:378: size=48 B, count=1, average=48 B
d:\miniconda3\lib\tracemalloc.py:281: size=40 B, count=1, average=40 B
d:\miniconda3\lib\sre_compile.py:572: size=40 B, count=1, average=40 B
d:\miniconda3\lib\site-packages\jupyter_client\session.py:855: size=32 B, count=1, average=32 B
d:\miniconda3\lib\asyncio\base_events.py:1416: size=32 B, count=1, average=32 B
d:\miniconda3\lib\site-packages\IPython\core\compilerop.py:150: size=28 B, count=1, average=28 B
d:\miniconda3\lib\linecache.py:95: size=28 B, count=1, average=28 B
可以看出内存使用上,字典还是较为占用空间的
__slots__
问题的引出
都是字典惹的祸
字典为了提升查询效率,必须用空间换时间
一般来说一个实例,属性多一点,都存储在字典中便于查询,问题不大
但是如果数百万个实例,那么字典占的空间就大了
这个时候,能不能把属性字典__dict__
省了?
Python提供了__slots__
class A:
X = 1
def __init__(self):
self.x = 5
self.y = 6
def show(self):
print(self.x, self.y, self.z)
a = A()
print(A.__dict__)
print(a.__dict__)
{'__module__': '__main__', 'X': 1, '__init__': <function A.__init__ at 0x000001DD4D402F28>, 'show': <function A.show at 0x000001DD4D402A60>, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
{'x': 5, 'y': 6}
思考
上面的字典,谁的字典是个问题?
实例多达百万个的时候,这么多存放实例属性的字典是个问题
class A:
X = 1
__slots__ = ('y', 'z')
def __init__(self):
self.y = 5
def show(self):
print(self.x, self.y)
a = A()
# a.show()
print('A', A.__dict__)
print(a.__slots__)
# a.newx = 5
A {'__module__': '__main__', 'X': 1, '__slots__': ('y', 'z'), '__init__': <function A.__init__ at 0x000001DD0FAE7510>, 'show': <function A.show at 0x000001DD0FAE7488>, 'y': <member 'y' of 'A' objects>, 'z': <member 'z' of 'A' objects>, '__doc__': None}
('y', 'z')
__slots__
告诉解释器,实例的属性都叫什么,一般来说,既然要节约内存,最好还是使用元组比较好。一旦类提供了__slots__
,就阻止实例产生__dict__
来保存实例的属性。
尝试为实例a动态增加属性
a.newx = 5
返回AttributeError: ‘A’ object has no attribute ‘newx’
说明实例不可以动态增加属性了
A.NEWX = 20,这是可以的,因为这个是类属性
继承
class A:
X = 1
__slots__ = ('y', 'z')
def __init__(self):
self.y = 5
def show(self):
print(self.x, self.y)
a = A()
# a.show()
print('A', A.__dict__)
# print('obj', a.__dict__)
print(a.__slots__)
class B(A):
pass
print('B', B().__dict__)
A {'__module__': '__main__', 'X': 1, '__slots__': ('y', 'z'), '__init__': <function A.__init__ at 0x000001DD4D402D08>, 'show': <function A.show at 0x000001DD4D402B70>, 'y': <member 'y' of 'A' objects>, 'z': <member 'z' of 'A' objects>, '__doc__': None}
('y', 'z')
B {}
__slots__
不影响子类实例,不会继承下去,除非子类里面自己也定义了__slots__
。
应用场景
使用需要构建在数百万以上众多对象,且内存容量较为紧张,实力的属性简单,固定且不用动态增加的场景。
可以使用tracemalloc看看内存使用的差异。建议使用stats = snapshot.statistics('filename')
查看总内存使用。
未实现和未实现异常
print(type(NotImplemented))
print(type(NotImplementedError))
# raise NotImplementedError
<class 'NotImplementedType'>
<class 'type'>
NotImplemented是个值,单值,是NotImplementedType的实例。
NotImplementedError是类型,是异常类,返回type
运算符重载中的反向方法
前面学习过运算符重载的方法,例如__add__
和__iadd__
class A:
def __init__(self, x):
self.x = x
def __add__(self, other):
print(self, 'add')
return self.x + other.x
def __iadd__(self, other):
print(self, 'iadd')
return A(self.x + other.x)
def __radd__(self, other):
print(self, 'radd')
return self.x + other.x
a = A(4)
b = A(5)
print(a, b)
print(a + b)
print(b + a)
b += a
a += b
<__main__.A object at 0x000001DD0FAEC898> <__main__.A object at 0x000001DD0FAEC8D0>
<__main__.A object at 0x000001DD0FAEC898> add
9
<__main__.A object at 0x000001DD0FAEC8D0> add
9
<__main__.A object at 0x000001DD0FAEC8D0> iadd
<__main__.A object at 0x000001DD0FAEC898> iadd
__radd__
方法根本没有执行过,为什么?
因为都是A的实例,都是调用__radd__
,无非就是实例a还是b调用而已。
测试一下 a + 1
class A:
def __init__(self, x):
self.x = x
def __add__(self, other):
print(self, 'add')
return self.x + other.x
def __iadd__(self, other):
print(self, 'iadd')
return A(self.x + other.x)
def __radd__(self, other):
print(self, 'radd')
return self.x + other.x
a = A(4)
a + 1
<__main__.A object at 0x000001DD0FAECBE0> add
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-19-54846f1118c4> in <module>
17
18 a = A(4)
---> 19 a + 1
<ipython-input-19-54846f1118c4> in __add__(self, other)
5 def __add__(self, other):
6 print(self, 'add')
----> 7 return self.x + other.x
8
9 def __iadd__(self, other):
AttributeError: 'int' object has no attribute 'x'
出现了AttributeError,因为1是int类型,没有x这个属性,还是A类的__add__
被执行了。
测试1 + a,运行结果如下
AttributeError Traceback (most recent call last)
<ipython-input-18-c4b5a5f46151> in <module>
17
18 a = A(4)
---> 19 1 + a
<ipython-input-18-c4b5a5f46151> in __radd__(self, other)
13 def __radd__(self, other):
14 print(self, 'radd')
---> 15 return self.x + other.x
16
17
AttributeError: 'int' object has no attribute 'x'
这次执行的是实例a的__radd__
方法
1 + a等价于1.__add__(a)
,也就是int.__add__(1, a)
, 而int类型实现了__add__
方法的,为什么却不抛出异常,而是执行了实例a的__radd__
方法?
再看一个例子
class A:
def __init__(self, x):
self.x = x
def __add__(self, other):
print(self, 'add')
return self.x + other.x
def __iadd__(self, other):
print(self, 'iadd')
return A(self.x + other.x)
def __radd__(self, other):
print(self, 'radd')
return self.x + other.x
class B: # 未实现`__add__`
def __init__(self, x):
self.x = x
a = A(4)
b = B(10)
print(a + b)
print(b + a)
<__main__.A object at 0x000001DD05CE1DD8> add
14
<__main__.A object at 0x000001DD05CE1DD8> radd
14
b + a等价于b.__add__(a)
,但是类B没有实现__add__
方法,就去找a的__radd__
方法
1 + a等价于1.__add__(a)
,而int类型实现了__add__
方法的,不过这个方法对于这种加法的返回值是NotImplemented,解释器发现是这个值,就会发起对第二操作对象的__radd__
方法的调用。
B类也等价于下面的实现
class B:
def __init__(self, x):
self.x = x
def __add__(self, other):
if isinstance(other, type(self)):
return self.x + other.x
else:
return NotImplemented
1 + a能解决吗?
class A:
def __init__(self, x):
self.x = x
def __add__(self, other):
print(self, 'add')
if hasattr(other, 'x'):
return self.x + other.x
else:
try:
x = int(other)
except:
x = 0
return self.x + x
def __iadd__(self, other):
print(self, 'iadd')
return A(self.x + other.x)
def __radd__(self, other):
print(self, 'radd')
return self + other
class B:
def __init__(self, x):
self.x = x
a = A(4)
b = B(10)
print(a + b)
print(b + a)
print(a + 2)
print(2 + a)
print(a + 'abc')
print('abc', a)
<__main__.A object at 0x000001DD058BA898> add
14
<__main__.A object at 0x000001DD058BA898> radd
<__main__.A object at 0x000001DD058BA898> add
14
<__main__.A object at 0x000001DD058BA898> add
6
<__main__.A object at 0x000001DD058BA898> radd
<__main__.A object at 0x000001DD058BA898> add
6
<__main__.A object at 0x000001DD058BA898> add
4
abc <__main__.A object at 0x000001DD058BA898>
‘abc’ + a,字符串也实现了__add__
方法,不过默认是处理不了和其他类型的加法,就返回NotImplemented。
仅位置参数
2019年10月14日,发布了Python3.8.0,提供了仅位置参数(Positional-only arguments).
函数的形参定义增加到了5种。(普通参数,可变位置参数,keyword-only参数,可变关键字参数和仅位置参数)
# 此代码只能在python3.8以后的版本中运行!!!
def add(x, y=5, /, z=6):
print(x + y + z)
add(1, 2, 3)
add(1, y=2, z=3)
add(x=1, y=2, z=3)
add(1, 3, z=5)
File "<ipython-input-24-1e49f622eab9>", line 1
def add(x, y=5, /, z=6):
^
SyntaxError: invalid syntax
Python的对象模型
在Python中,任何对象都有模型,可以使用type()或者__class__
查看。
但是类型也是对象,即类对象,它也有自己的类型
所有新类型的缺省类型是type(可以使用元类来改变)
特殊类型type是所有对象的缺省类型,也包括type自己。但它又是一个对象, 因此从object继承
特殊类型object是继承树的顶层,它是python所有类型的最终基类
也就是说,继承都来自object,类型都看type。type也是对象,继承自object,object也有类型是type。
这俩又特殊,type类型使它自己,object没有基类。
PEP(Python Enhancement Proposals) — python增强提案
上一篇: 大厂前端经典面试问题精选(附答案)
下一篇: 通用算法之归并求逆序对