Python 魔法方法(五) 从__get__,__set__, __delete__再探属性访问顺序

这里需要先说一下描述符的概念。

描述符:描述符本质就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议。

描述符分数据描述符,只有__get__的描述符是非数据描述符,有__get__和__set__的描述符是数据描述符

 

__get__(self, instance, owner)—获取属性时调用,返回设置的属性值,通常是_set_中的value,或者附加的其他组合值。
 __set__(self, instance, value) — 设置属性时调用,返回None.
 __delete__(self, instance)— 删除属性时调用,返回None


其中,instance是这个描述符属性所在的类的实例,而owner是描述符所在的类。

看下面例子

class A(object):
    name = "unchange"

    def  __init__(self, value):
        print "into A __init__"
        self.value = value

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

class B(object):
    value = A(10)

    def __init__(self, value):
        print "into B __init__"

b = B(20)
# into A __init__
# into B __init__
print b.value
# into __get__
# <__main__.B object at 0x0000000001E13710> <class '__main__.B'>
# None

也就是,instance和owner就是类B的东西。

 

上一篇,我们讲到,当没有数据描述符是,实例访问属性的顺序是这样的:

当访问实例属性a.name时,其访问顺讯是:(假设没有数据描述符)

先进入__getattribute__

1、实例属性a.__dict__  2、类属性A.__dict__ 3、父类及基类属性A.__bases__.__dict

搜索不到再进入__getattr__

 

当有数据描述符,或非数据描述符时,访问顺序变为:

先进入__getattribute__

1、如果该属性是数据描述符,进入__get__  2、实例属性  3、非数据描述符

搜索不到再进入__getattr__

 

class A(object):

    def  __init__(self, name):
        print "into A __init__"
        self.name = name

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

    def __set__(self, instance, value):
        print "into __set__"


class B(object):
    name = A("Tom") #假如没有这个数据描述符,输出结果就是“Bob”

    def __init__(self, name, age):
        print "into B __init__"
        self.age = age
        self.name = name

    def __getattribute__(self, item):
        print "into __getattribute__"
        return object.__getattribute__(self, item)

b = B("Bob", 100)
# into A __init__
# into B __init__
# into __set__
print b.name
# into __getattribute__
# into __get__
# None

有数据描述符,依然会进入__getattribute__,然后进入__get__。若没有数据描述符,直接访问实例属性。

原因:在初始化__init__开始前,就开始对数据描述符name,即A(“Tom”)进行实例化。

当进入B的实例化的时候,已经对self.name进行了赋值,因为描述符的赋值操作会进入__set__(这里实际上__set__只做了打印操作,没有帮instance赋值)。

让实例b的__dict__里没有name这个属性,当然没办法在实例b得到想要的属性。

 

让A中的__set__方法不写时,name就是非数据描述符。

例如

class A(object):

    def  __init__(self, name):
        print "into A __init__"
        self.name = name

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



class B(object):
    name = A("Tom") #假如没有这个数据描述符,输出结果就是“Bob”

    def __init__(self, name, age):
        print "into B __init__"
        self.age = age
        self.name = name

    def __getattribute__(self, item):
        print "into __getattribute__"
        return object.__getattribute__(self, item)

b = B("Bob", 100)
# into A __init__
# into B __init__
# into __set__
print b.name
# into __getattribute__
# Bob

非数据描述符的优先级低于实例属性。

 

相关用法:1、属性访问、修改控制

class A(object):

    def  __init__(self, name):
        print "into A __init__"
        self.name = name

    def __get__(self, instance, owner):
        print "into __get__"
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print "into __set__"
        if value < 0:
            raise ValueError
        instance.__dict__[self.name] = value

class B(object):
    name = A("name")
    age = A("age")

    def __init__(self, name, age):
        print "into B __init__"
        self.age = age
        self.name = name

    def __getattribute__(self, item):
        print "into __getattribute__"
        return object.__getattribute__(self, item)

b = B("Bob", 20)
b.age = -1
print b.age

用__set__拦截赋值操作,不合理的值会抛错。__delete__的用法与__set__类似,在删除的时候拦截删除操作。

数据描述符跟@property一样具有属性访问控制的效果,但数据描述符能用于更多的属性控制,且不显代码累赘。

 

需要注意的地方:

1、重写__get__,__set__,__delete__避免进入死循环。

2、描述符被__getattribute()方法调用,重载__getattribute__()不当会阻止描述符自动调用。

3、数据描述符会重载实例字典,非数据描述符可能会被实例字典重载(属性访问顺序的原因)。

 

 

 

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
好的,下面是Python基于Pymssql模块实现通讯录管理系统的教程。 1.安装Pymssql模块 ``` pip install pymssql ``` 2.创建数据库和表 首先需要在SQL Server中创建一个数据库,并且创建一个名为“contacts”的表,用于存储联系人的信息。 ```sql CREATE DATABASE ContactDB; GO USE ContactDB; GO CREATE TABLE contacts ( id INT PRIMARY KEY IDENTITY, name VARCHAR(50) NOT NULL, phone VARCHAR(50) NOT NULL, email VARCHAR(50) NOT NULL ); GO ``` 3.连接数据库并实现增删改查功能 ```python import pymssql class Contacts: def __init__(self, server, username, password, database): self.server = server self.username = username self.password = password self.database = database def connect(self): self.conn = pymssql.connect(server=self.server, user=self.username, password=self.password, database=self.database) def add_contact(self, name, phone, email): self.connect() cursor = self.conn.cursor() sql = "INSERT INTO contacts (name, phone, email) VALUES (%s, %s, %s)" cursor.execute(sql, (name, phone, email)) self.conn.commit() self.conn.close() def get_contacts(self): self.connect() cursor = self.conn.cursor() sql = "SELECT * FROM contacts" cursor.execute(sql) rows = cursor.fetchall() self.conn.close() return rows def update_contact(self, id, name, phone, email): self.connect() cursor = self.conn.cursor() sql = "UPDATE contacts SET name=%s, phone=%s, email=%s WHERE id=%s" cursor.execute(sql, (name, phone, email, id)) self.conn.commit() self.conn.close() def delete_contact(self, id): self.connect() cursor = self.conn.cursor() sql = "DELETE FROM contacts WHERE id=%s" cursor.execute(sql, (id,)) self.conn.commit() self.conn.close() ``` 4.编写用户交互界面 ```python def main(): contacts = Contacts(server='localhost', username='sa', password='password', database='ContactDB') while True: print("1. 添加联系人") print("2. 查看联系人") print("3. 修改联系人") print("4. 删除联系人") print("5. 退出") choice = input("请输入要执行的操作:") if choice == '1': name = input("请输入姓名:") phone = input("请输入电话:") email = input("请输入邮箱:") contacts.add_contact(name, phone, email) print("联系人添加成功!\n") elif choice == '2': rows = contacts.get_contacts() print("所有联系人:") for row in rows: print(row) elif choice == '3': id = input("请输入要修改的联系人ID:") name = input("请输入姓名:") phone = input("请输入电话:") email = input("请输入邮箱:") contacts.update_contact(id, name, phone, email) print("联系人修改成功!\n") elif choice == '4': id = input("请输入要删除的联系人ID:") contacts.delete_contact(id) print("联系人删除成功!\n") elif choice == '5': exit() else: print("请输入正确的操作!\n") if __name__ == '__main__': main() ``` 以上就是Python基于Pymssql模块实现通讯录管理系统的教程,希望能对你有所帮助。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值