__get__和__set__引发的关于python属性获取与设置的顺序探索

    之前开始接触python的时候,阅读过python的入门书籍,那个时候就看过关于python的描述符相关的基本知识,而描述又涉及到属性的获取和设置问题,那个时候可以说是对这部分的介绍知识浏览过去,有个印象。但是最近在阅读项目代码的时候,遇到了__get__和__set__相关功能模块的设计。埃?之前写代码的时候几乎不用这两个内置函数,有什么作用呢?然后一查发现是涉及到了python的描述符,记得之前看过,知道这个概念,现在又遇到了,干脆话点时间把这块知识理解掌握。

    开始探究之前,先要把python描述符的基本概念了解一下。描述符:如果一个类定义了__get__()、__set__()和__del__()(这篇文章不讨论这个函数)函数,那么就称之为没描述符。描述符分为非数据描述符(只实现了__get__())和数据描述符(同时实现了__get__()和__set__())。

    根据官方文档的介绍:When a class attribute is a descriptor, its special binding behavior is triggered upon attribute lookup. Normally, using a.b to get, set or delete an attribute looks up the object named b in the class dictionary for a, but if b is a descriptor, the respective descriptor method gets called。当一个类的属性是描述符的时候,使用a.b在a中查找b(假设不是描述符)就会调用调用b的__get__的返回值。但是这里要注意的是,描述符是伴随类而存在的,也就是说只有类的属性是一个没描述符的时候,才会又函数__get__或者__set__函数,在类实例中的属性是描述符的时候,调用对应属性是不会触发函数。那么就会有几个疑问需要解决。

    ①类对数据描述符和非数据描述符属性的获取和赋值分别会有什么表现;

    ②类实例对数据描述符和非数据描述符属性的获取和赋值分别会有什么表现;

    ③ 类实例的命名空间中存在与数据描述符或者非数据描述符相同的属性项时有什么表现;

class Descriptor1(object):
	def __init__(self, name):
		self.name = name

	def __get__(self, instance, owner):
		print "__get__ method"
		print instance
		print owner

class Descriptor2(Descriptor1):
	def __set__(self, instance, val):
		print "__set__ method"
		print instance
		print val

这里通过实现定义两个描述符,一个是数据描述符,另外一个是非数据描述符。然后以这两个描述符对疑问一个个进行解决。

类对数据描述符和非数据描述符属性的获取和赋值分别会有什么表现:

1、类对非数据描述符属性的获取及赋值:

class Demo(object):
	prop = Descriptor1("des")

print Demo.prop

Demo.prop = "prop"
print Demo.prop


"""
>>> reload(attr_test)
__get__ method
None
<class 'samples.attr_test.Demo'>
None
prop
"""

表现:类获取描述符属性时,会触发描述符的__get__函数。而对描述符属性赋值的时候,是真正的赋值,将类属性名直接引用了新的值

2、类对数据描述符属性的获取及赋值:这个的表现跟上面的表现是一样,就不再列出代码。

类实例对数据描述符和非数据描述符属性的获取和赋值分别会有什么表现:

1、类实例对非数据描述符属性的获取及赋值:

class Demo(object):
	prop = Descriptor1("des")

demo = Demo()

print "demo.__dict__:", demo.__dict__

print "demo.prop:", demo.prop

demo.prop = "hello"

print "demo.__dict__", demo.__dict__

"""
demo.__dict__: {}
demo.prop: __get__ method
<samples.attr_test.Demo object at 0x00000000041E0AC8>
<class 'samples.attr_test.Demo'>
None
demo.__dict__ {'prop': 'hello'}
"""

2、类实例对数据描述符属性的获取以及赋值:

class Demo(object):
	prop = Descriptor2("des")

demo = Demo()

print "demo.__dict__:", demo.__dict__

print "demo.prop:", demo.prop

demo.prop = "hello"

print "demo.__dict__", demo.__dict__

"""
demo.__dict__: {}
demo.prop: __get__ method
<samples.attr_test.Demo object at 0x00000000041E0BA8>
<class 'samples.attr_test.Demo'>
None
__set__ method
<samples.attr_test.Demo object at 0x00000000041E0BA8>
hello
demo.__dict__ {}
"""

表现总结:通过对比实例对非数据和数据描述符的获取及赋值可以得到,类实例在获取属性的时候,如果属性是描述符,则会调用__get__,在赋值的时候,对于非数据描述符,没有__set__函数,则直接在实例的命名空间添加一个与描述符同名的属性,而且改属性旨在当前实例具有;对于数据描述符的话,则会调用对应描述符的__set__函数。

 类实例的命名空间中存在与数据描述符或者非数据描述符相同的属性项时有什么表现:

1、类实例的命名空间中具有与非数据描述符同名的属性:

class Demo(object):
	prop = Descriptor1("des")
	def __init__(self):
		self.prop = "hello world"

demo = Demo()

print "demo.__dict__:", demo.__dict__

print "demo.prop:", demo.prop

demo.prop = "hello"

print "demo.__dict__", demo.__dict__

"""
demo.__dict__: {'prop': 'hello world'}
demo.prop: hello world
demo.__dict__ {'prop': 'hello'}
"""

2、类实例的命名空间中存在与数据描述符同名的属性:

class Demo(object):
	prop = Descriptor2("des")
	def __init__(self):
		self.prop = "hello world"

demo = Demo()

print "demo.__dict__:", demo.__dict__

print "demo.prop:", demo.prop

demo.prop = "hello"

print "demo.__dict__", demo.__dict__

"""
__set__ method
<samples.attr_test.Demo object at 0x00000000041E0E10>
hello world
demo.__dict__: {}
demo.prop: __get__ method
<samples.attr_test.Demo object at 0x00000000041E0E10>
<class 'samples.attr_test.Demo'>
None
__set__ method
<samples.attr_test.Demo object at 0x00000000041E0E10>
hello
demo.__dict__ {}
"""

表现总结:通过对比,在获取属性或者给属性赋值的时候,是按照数据描述符,命名空间,非数据描述符的顺序进行的。在获取属性值的时候,如果属性是数据描述符,则调用数据描述符的__get__函数返回值作为属性值,如果不是数据描述符,则会在命名空间中寻找对应的属性,直接返回属性值;如果明明空间中也没有的话,则查找非数据描述符,调用非数据描述符的__get__函数且返回值作为属性值。

在解决了这几个主要问题之后,我也在网上搜集了关于python获取属性或者给属性赋值的相关顺序的内容:然后结合我这几个问题的解决,能够进一步掌握关于描述符、python属性寻找策略的相关知识,就先写到这了。

好的,下面是《Python游戏趣味编程》第10章拼图游戏的代码: ```python import pygame import random import os # 设置游戏窗口的大小 WINDOW_WIDTH = 480 WINDOW_HEIGHT = 640 # 设置游戏所需的图片路径 IMAGE_PATH = 'images' # 设置游戏所需的图片名称 IMAGE_NAMES = ['bird0.png', 'bird1.png', 'bird2.png', 'bird3.png', 'background.png'] # 设置拼图的行数和列数 ROW_COUNT = 3 COL_COUNT = 3 # 初始化游戏 pygame.init() # 创建游戏窗口 window = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT)) # 加载游戏所需的图片 images = [] for name in IMAGE_NAMES: path = os.path.join(IMAGE_PATH, name) image = pygame.image.load(path).convert_alpha() images.append(image) # 获取每个拼图块的宽度和高度 block_width = WINDOW_WIDTH // COL_COUNT block_height = WINDOW_HEIGHT // ROW_COUNT # 创建拼图块列表 blocks = [] for row in range(ROW_COUNT): for col in range(COL_COUNT): x = col * block_width y = row * block_height rect = pygame.Rect(x, y, block_width, block_height) image = images[-1].subsurface(rect) block = {'rect': rect, 'image': image, 'row': row, 'col': col} blocks.append(block) # 打乱拼图块的顺序 random.shuffle(blocks) # 创建游戏时钟 clock = pygame.time.Clock() # 循环执行游戏 while True: # 处理游戏事件 for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() exit() # 绘制游戏背景 window.blit(images[-1], (0, 0)) # 绘制拼图块 for block in blocks: window.blit(block['image'], block['rect']) # 判断是否完成拼图 for index, block in enumerate(blocks): row = index // COL_COUNT col = index % COL_COUNT if block['row'] != row or block['col'] != col: break else: font = pygame.font.Font(None, 64) text = font.render('You Win!', True, (255, 0, 0)) x = (WINDOW_WIDTH - text.get_width()) // 2 y = (WINDOW_HEIGHT - text.get_height()) // 2 window.blit(text, (x, y)) # 更新游戏画面 pygame.display.update() # 控制游戏帧率 clock.tick(60) ``` 这个代码实现了一个简单的拼图游戏,包括以下功能: 1. 加载游戏所需的图片; 2. 将图片切割成若干个拼图块; 3. 打乱拼图块的顺序; 4. 绘制游戏背景和拼图块; 5. 判断拼图是否完成; 6. 显示游戏结束提示; 7. 控制游戏帧率。 需要注意的是,这只是一个简单的示例代码,实际的拼图游戏需要更加丰富的交互和功能,例如拖拽拼图块、计时、计分等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值