欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

知识补充

程序员文章站 2022-03-03 07:50:48
...

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增强提案