知识补充

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值