python 高级知识(待分发)

  1. range 不是迭代器
    https://www.cnblogs.com/amize/p/14020050.html
  • type 类
    • 不管是Python自带的像“int”、“list”等类对象,还是自定义的类对象(注意:是类对象,不是实例对象),都是属于type类。
#1. type 类定义的方法 (简单)
class Foo(object):
    version = '1.0.1'
    pass


#注意(object,)的写法, class type(name,bases,dict)
Foo = type('Foo',(object,), dict(version='1.0.1'))
# Foo = type('Foo',(object,), {"version":'1.0.1'})
# print((1,)) # 元祖
# print((1))   # 数字


#type函数判断对象类型,还可以通过__class__属性获取
/*
>>> a = 1
>>> a.__class__
<class 'int'>
>>> a.__class__.__class__
<class 'type'>
>>> a.__class__.__class__.__class__
<class 'type'>
*/


#2. type 类定义的方法 (复杂)
#class 创建
class A(object):
    # 类属性
    role = 'student'

    # 实例方法
    def __init__(self, name):
        # 实例属性
        self.name = name

    # 类方法
    @classmethod
    def study(cls):
        pass

    # 静态方法
    @staticmethod
    def cal_student_num():
        pass

#type 创建
A = type(
    'A',
    (object,),
    {
        'role': 'student',
        '__init__': __init__,
        'study': study,
        'cal_student_num': cal_student_num
    })

class type(name,bases,dict)
name参数变成__name__对象属性,bases参数变成 __bases__ 对象属性,dict参数变成__dict__对象属性,分别查看例子中的这三个属性:
In [6]: A.__class__
Out[6]: type

In [7]: A.__bases__
Out[7]: (object,)

In [8]: A.__dict__
Out[8]:
mappingproxy({'__dict__': <attribute '__dict__' of 'A' objects>,
              '__doc__': None,
              '__init__': <function __main__.__init__>,
              '__module__': '__main__',
              '__weakref__': <attribute '__weakref__' of 'A' objects>,
              'cal_student_num': <staticmethod at 0x10387ac88>,
              'role': 'student',
              'study': <classmethod at 0x10387ac18>})


  1. 类装饰器
# 1
def attr_upper(cls):
    for attrname,value in cls.__dict__.items():
        if isinstance(value,str):
            if not value.startswith('__'):
                setattr(cls,attrname,bytes.decode(str.encode(value).upper()))
    return cls    

@attr_upper
class Person:
    sex = 'man'

print(Person.sex) # MAN


# 2
def decorator(cls):
      class NewClass(cls):
            attr = 100
      return NewClass

@decorator
class X:
       pass

>>> X.attr
100


# 3 robot中实际应用
def py2to3(cls):
    if hasattr(cls, '__unicode__'):
        cls.__str__ = lambda self: self.__unicode__()
    if hasattr(cls, '__nonzero__'):
        cls.__bool__ = lambda self: self.__nonzero__()
    return cls

@py2to3
class ItemList(object):
  ...

  1. 描述符
晕 描述符,在描述符方法中不能使用 getattr,setattr 。只能直接访问 __dict__,否则会造成死递归,因为这两个方法内部调用的就是 __get__,__set__。
https://segmentfault.com/a/1190000020867200

调用顺序: 不理解 20210230
1. __get__  # object.__get__(self, instance, owner=None)
instance:所有者类实例
owner:   所有者类
该方法应返回计算出的属性值或引发AttributeError异常。该方法在所有者实例的 __dict__ 之后调用。
例子:
class A:
    def __get__(self, instance, owner):
        return 55

class B:
    value = A()

b = B()

b.__dict__  # >>> {}
b.value     # >>> 55

b.value = 99
b.__dict__  # >>> {'value': 99}
b.value     # >>> 99

del b.value
b.value     # >>> 55
b.__dict__  # >>> {}



2. __set__   # object.__set__(self, instance, value)
instance:所有者类实例
数据描述符使得所有描述符方法在所有者实例的 __dict__ 之前调用。
例子:
class A:
    def __get__(self, instance, owner):   # B object 实例 和 class B类 ,注意都不是A相关的
        if instance is None:
            return self
        return 55

    def __set__(self, instance, value):
        instance.__dict__[self._name] = value

    def __set_name__(self, owner, name):
        self._name = name  # 获得name = b

class B:
    value = A()
    
b = B()

b.value = 99
b.__dict__   # >>> {'value': 99}
b.value      # >>> 55            # 和上面的 1. __get__ 一起理解 ,可能是因为这里重写了 __set__导致的



3. __delete__  # object.__delete__(self, instance)
instance:所有者类实例
在描述符实现了 __set__ 方法的情况下使用,对所有者实例调用 del 语句会调用描述符的 __delete__ 方法,如果没有就会报错:AttributeError:
例子:
class A:
    def __get__(self, instance, owner):
        if instance is None:
            return self
        return 55
    def __set__(self, instance, value):
        instance.__dict__[self._name] = value
    def __set_name__(self, owner, name):
        self._name = name

class B:
    value = A()
    
b = B()

b.value = 99
b.__dict__     # >>> {'value': 99}
b.value        # >>> 55
del b.value    # >>> AttributeError: __delete__




4. __set_name__ (Python 3.6 新增) # object.__set_name__(self, owner, name)
owner:所有者类
name:该变量会获取到所有者类属性的属性名
例子:
class A:
    def __get__(self, instance, owner):
        return 55
    def __set_name__(self, owner, name):
        print(owner)
        print(name)
        self._name = name
class B:
    this_is_name = A()
# <class '__main__.B'>
# this_is_name




# 描述符,多个所有者实例的同一属性引用的是同一描述符对象:

class A:
    def __get__(self, instance, owner):
        if instance is None:
            return self
        return 55
    def __set__(self, instance, value):
        instance.__dict__[self._name] = value
    def __set_name__(self, owner, name):
        self._name = name
class B:
    value = A()
    
b1 = B()
b2 = B()

id(b1) == id(b2)              # >>> False
id(b1.value) == id(b2.value)  # >>> True





# 元编程概念 ,https://zhuanlan.zhihu.com/p/29849145  

1. 如果我们想让某一些类拥有某些相同的特性,我们可以自定义一个元类,然后让它成为这些类的元类。
2. 如果我们想让某一些函数拥有某些相同的功能,又不想把代码复制粘贴一遍,我们可以定义一个装饰器。
3. 假如我们想让【实例的属性】拥有某些共同的特点呢?
    a.可以用property,但是这些逻辑必须在每个类定义的时候都写一遍。
    b.如果我们想让这些类的实例的某些属性都有相同的特点的话,就可以自定义一个描述符类。


# 1 简单示例 (Person类的age属性必须是int类型)
class TypedField:
    def __init__(self, _type):
        self._type = _type

    def __get__(self, instance, cls):
        if instance is None:
            return self
        else:
            return getattr(instance, self.name)

    def __set_name__(self, cls, name):
        self.name = name

    def __set__(self, instance, value):
        if not isinstance(value, self._type):             # isinstance('15',int)
            raise TypeError('Expected' + str(self._type))
        instance.__dict__[self.name] = value


class Person:
    age = TypedField(int)
    name = TypedField(str)

    def __init__(self, age, name):
        self.age = age
        self.name = name

jack = Person(15, 'Jack')
jack.age = '15' # 会报错

TypedField是一个描述符类,Person的属性是描述符类的实例,看似描述符是作为Person,也就是类的属性而不是实例属性存在的。但实际上,一旦Person的实例访问了同名的属性,描述符就会起作用。
需要注意的是,在Python3.5及之前的版本中,是没有__set_name__这个特殊方法的,这意味着如果你想要知道在类定义中描述符被起了一个什么样的名字,是需要在描述符实例化时显式传递给它的,也就是需要多一个参数。
不过在Python3.6中,这个问题得到了解决,只需要在描述符类定义中重写__set_name__这个方法就好了。
还需要注意的是__get__的写法,基本上对instance的判断是必需的,不然会报错。原因也不难理解,就不细说了。



# 2 示例来自 Clean Code in Python,https://segmentfault.com/a/1190000020867200
# 具有当前城市的旅行者在程序运行期间跟踪用户访问过的所有城市
class HistoryTracedAttribute:    # 描述符类
    def __init__(self, trace_attribute_name):
        self.trace_attribute_name = trace_attribute_name
        self._name = None

    def __set_name__(self, owner, name):
        self._name = name

    def __get__(self, instance, owner):
        if instance is None:
            return self
        return instance.__dict__[self._name]

    def __set__(self, instance, value):
        self._track_change_in_value_for_instance(instance, value)
        instance.__dict__[self._name] = value

    def _track_change_in_value_for_instance(self, instance, value):
        self._set_default(instance)
        if self._needs_to_track_change(instance, value):
            instance.__dict__[self.trace_attribute_name].append(value)

    def _needs_to_track_change(self, instance, value) -> bool:
        try:
            current_value = instance.__dict__[self._name]
        except KeyError:
            return True
        return value != current_value

    def _set_default(self, instance):
        instance.__dict__.setdefault(self.trace_attribute_name, [])

class Traveller:    # 所有者类
    current_city = HistoryTracedAttribute("cities_visited")  # 描述符类一定要是所有者类的类属性
    def __init__(self, name, current_city):
        self.name = name
        self.current_city = current_city




# robot中 应用
class setter(object):

    def __init__(self, method):
        self.method = method
        self.attr_name = '_setter__' + method.__name__
        self.__doc__ = method.__doc__

    def __get__(self, instance, owner):
        if instance is None:
            return self
        try:
            return getattr(instance, self.attr_name)
        except AttributeError:
            raise AttributeError(self.method.__name__)

    def __set__(self, instance, value):
        if instance is None:
            return
        setattr(instance, self.attr_name, self.method(instance, value))



@setter
def keywords(self, keywords):
    """Suite setup and teardown as a :class:`~.Keywords` object."""
    return Keywords(self.keyword_class, self, keywords)

posted @ 2020-12-06 10:49  该显示昵称已被使用了  阅读(89)  评论(0编辑  收藏  举报