Python 经验 - 对象引用/可变性/垃圾回收/元类编程

2018-08-22  本文已影响5人  千反田爱瑠爱好者

变量

在Python中变量的本质是一个指针(类比为可贴在任何物品上的标签,Java中则可以类比为盒子)。

a = 1           # 生成对象,把a指向该对象
a = "abc"       # 把a指向别的对象

a = [1,2,3]
b = a
print (id(a), id(b))
print (a is b)
b.append(4)       # 相当于在列表上贴上a、b两个标签,对a/b操作都是在同一个对象上完成
print (a)

# ========

a = [1,2,3,4]       # not a is b 此时a、b可理解为贴在不同对象上的标签
b = [1,2,3,4]       # a == b,表示a、b所指的对象中值是相等的

a = 1               
b = 1               # a is b,a、b都是从一个常量池获取,所以是同一个对象

class People:
    pass

person = People()
if type(person) is People:
    print("yes")

一个典型的传参错误问题

向函数传入list、dict,对象可能会被修改。

def add(a, b):
    a += b
    return a

class Company:
    def __init__(self, name, staffs=[]):
        self.name = name
        self.staffs = staffs

    def add(self, staff_name):
        self.staffs.append(staff_name)

    def remove(self, staff_name):
        self.staffs.remove(staff_name)

if __name__ == "__main__":
    com1 = Company("com1", ["ywh1", "ywh2"])
    com1.add("ywh3")
    com1.remove("ywh1")
    print(com1.staffs)

    com2 = Company("com2")
    com2.add("ywh")
    print(com2.staffs)

    print(Company.__init__.__defaults__)

    com3 = Company("com3")
    com3.add("ywh5")
    print(com2.staffs)
    print(com3.staffs)
    print(com2.staffs is com3.staffs)

    a = 1
    b = 2
    c = add(a, b)
    print(a, b, c)  # 1, 2, 3

    a = [1, 2]
    b = [3, 4]
    c = add(a, b)
    print(a, b, c)  # [1, 2, 3, 4], [3, 4], [1, 2, 3, 4]

    a = (1, 2)
    b = (3, 4)
    c = add(a, b)
    print(a, b, c)  # (1, 2) (3, 4) (1, 2, 3, 4)

垃圾回收

Python的GC是基于引用计数标记-清除机制分代技术,详情见:https://github.com/yipwinghong/interview_python

a = object()
b = a
del a
print(b)
print(a)
class A:
    def __del__(self):
        pass

元类编程

property动态属性

不建议直接访问成员变量,而使用property实现get、set方法,通过代码逻辑控制访问权限。

from datetime import date, datetime
class User:
    def __init__(self, name, birthday):
        self.name = name
        self.birthday = birthday
        self._age = 0

    # def get_age(self):
    #     return datetime.now().year - self.birthday.year

    @property
    def age(self):
        return datetime.now().year - self.birthday.year

    @age.setter
    def age(self, value):
        self._age = value

if __name__ == "__main__":
    user = User("ywh", date(year=1987, month=1, day=1))
    user.age = 30
    print (user._age)
    print(user.age)

getattr与getattribute

# __getattr__, __getattribute__
from datetime import date
class User:
    def __init__(self,info={}):
        self.info = info
    
    # 在__getattribute__抛出异常时调用
    def __getattr__(self, item):
        return self.info[item]

    # 是__getattr__更高级的封装,使用obj.attr但查找不到属性的时候调用,但尽量不要重写
    def __getattribute__(self, item):
        return "ywh"

if __name__ == "__main__":
    user = User(
        info={"company_name": "alibaba", "name": "ywh"}
    )
    print(user.test)

属性描述符

利用属性描述符做参数类型检查(类似Django的做法)

from datetime import date, datetime
import numbers

# 属性描述符(__get__、__set__、__delete__实现任意一或多个)
class IntField:
    def __get__(self, instance, owner):
        return self.value
    
    def __set__(self, instance, value):
        if not isinstance(value, numbers.Integral):
            raise ValueError("int value need")
        if value < 0:
            raise ValueError("positive value need")
        self.value = value
    
    def __delete__(self, instance):
        pass

class NonDataIntField:
    # 非数据属性描述符
    def __get__(self, instance, owner):
        return self.value

class User:
    age = IntField()
    # age = NonDataIntField()

'''
    如果user是某个类的实例,那么user.age(以及等价的getattr(user,’age’))
    首先调用__getattribute__。如果类定义了__getattr__方法,
    那么在__getattribute__抛出 AttributeError 的时候就会调用到__getattr__,
    而对于描述符(__get__)的调用,则是发生在__getattribute__内部的。
    user = User(), 那么user.age 顺序如下:
    (1)如果“age”是出现在User或其基类的__dict__中, 且age是data descriptor, 那么调用其__get__方法, 否则
    (2)如果“age”出现在user的__dict__中, 那么直接返回 obj.__dict__[‘age’], 否则
    (3)如果“age”出现在User或其基类的__dict__中
    (3.1)如果age是non-data descriptor,那么调用其__get__方法, 否则
    (3.2)返回 __dict__[‘age’]
    (4)如果User有__getattr__方法,调用__getattr__方法,否则
    (5)抛出AttributeError
'''

# class User:
#
#     def __init__(self, name, email, birthday):
#         self.name = name
#         self.email = email
#         self.birthday = birthday
#         self._age = 0
#
#     # def get_age(self):
#     #     return datetime.now().year - self.birthday.year
#
#     @property
#     def age(self):
#         return datetime.now().year - self.birthday.year
#
#     @age.setter
#     def age(self, value):
#         #检查是否是字符串类型
#         self._age = value

if __name__ == "__main__":
    user = User()
    user.__dict__["age"] = "abc"
    print (user.__dict__)
    print (user.age)
    # print (getattr(user, 'age'))
    # user = User("ywh", date(year=1987, month=1, day=1))
    # user.age = 30
    # print (user._age)
    # print(user.age)

__new____init__

class User:

    def __new__(cls, *args, **kwargs):
        print (" in new ")
        return super().__new__(cls)
    
    def __init__(self, name):
        print (" in init")
        pass
        
a = int()
if __name__ == "__main__":
    user = User(name="ywh")

元类

def create_class(name):
    if name == "user":
        class User:
            def __str__(self):
                return "user"
        return User
    elif name == "company":
        class Company:
            def __str__(self):
                return "company"
        return Company

def say(self):
    return "i am user"
    # return self.name


class BaseClass():
    def answer(self):
        return "i am baseclass"

# 使用元类控制类的实例化过程
from collections.abc import *

class MetaClass(type):
    def __new__(cls, *args, **kwargs):
        return super().__new__(cls, *args, **kwargs)

class User(metaclass=MetaClass):
    
    def __init__(self, name):
        self.name = name
    
    def __str__(self):
        return "user"

if __name__ == "__main__":
    # MyClass = create_class("user")
    # my_obj = MyClass()
    # print(type(my_obj))
    
    # type动态创建类
    User = type(
        "User", 
        (BaseClass, ), 
        {"name":"user", "say":say}
    )   
    my_obj = User(name="ywh")
    print(my_obj)

实例:实现简易ORM

import numbers

class Field:
    pass

# 整型描述符
class IntField(Field):

    def __init__(self, db_column, min_value=None, max_value=None):
        self._value = None
        self.min_value = min_value
        self.max_value = max_value
        self.db_column = db_column
        if min_value :
            if not isinstance(min_value, numbers.Integral):
                raise ValueError("min_value must be int")
            elif min_value < 0:
                raise ValueError("min_value must be positive int")
        if max_value :
            if not isinstance(max_value, numbers.Integral):
                raise ValueError("max_value must be int")
            elif max_value < 0:
                raise ValueError("max_value must be positive int")
        if min_value  and max_value :
            if min_value > max_value:
                raise ValueError("min_value must be smaller than max_value")

    def __get__(self, instance, owner):
        return self._value

    def __set__(self, instance, value):
        if not isinstance(value, numbers.Integral):
            raise ValueError("int value need")
        if value < self.min_value or value > self.max_value:
            raise ValueError("value must between min_value and max_value")
        self._value = value

# 字符串型描述符
class CharField(Field):
    def __init__(self, db_column, max_length=None):
        self._value = None
        self.db_column = db_column
        if not max_length:
            raise ValueError("you must spcify max_lenth for charfiled")
        self.max_length = max_length

    def __get__(self, instance, owner):
        return self._value

    def __set__(self, instance, value):
        if not isinstance(value, str):
            raise ValueError("string value need")
        if len(value) > self.max_length:
            raise ValueError("value len excess len of max_length")
        self._value = value


class ModelMetaClass(type):

    def __new__(cls, name, bases, attrs, **kwargs):
        if name == "BaseModel":
            return super().__new__(cls, name, bases, attrs, **kwargs)
        fields = {}
        for key, value in attrs.items():
            if isinstance(value, Field):
                fields[key] = value
        attrs_meta = attrs.get("Meta", None)
        _meta = {}
        db_table = name.lower()
        if attrs_meta :
            table = getattr(attrs_meta, "db_table", None)
            if table :
                db_table = table
        _meta["db_table"] = db_table
        attrs["_meta"] = _meta
        attrs["fields"] = fields
        del attrs["Meta"]
        return super().__new__(cls, name, bases, attrs, **kwargs)


class BaseModel(metaclass=ModelMetaClass):
    def __init__(self, *args, **kwargs):
        for key, value in kwargs.items():
            setattr(self, key, value)
        return super().__init__()

    def save(self):
        fields = []
        values = []
        for key, value in self.fields.items():
            db_column = value.db_column
            if not db_column:
                db_column = key.lower()
            fields.append(db_column)
            value = getattr(self, key)
            values.append(str(value))

        sql = "insert {db_table}({fields}) values({values})".format(
            db_table=self._meta["db_table"],
            fields=",".join(fields),
            values=",".join(values)
        )

class User(BaseModel):

    # 数据库表中的字段
    name = CharField(db_column="name", max_length=10)
    age = IntField(db_column="age", min_value=1, max_value=100)

    # 表名称
    class Meta:
        db_table = "user"

if __name__ == "__main__":
    user = User(name="ywh", age=28)
    user.save()
上一篇下一篇

猜你喜欢

热点阅读