pythoncookbook3 第8章 类与对象

创建缓存实例--单例模式

# import logging
# a = logging.getLogger('foo')
# c = logging.getLogger('foo')
# b = logging.getLogger('bar')
# print(a is c)

# class Spam:
# def __init__(self,name):
# self.name = name

import weakref

# _cache = weakref.WeakValueDictionary()
#
# def get_spam(name):
# if name not in _cache:
# s = Spam(name)
# _cache[name] = s
# else:
# s = _cache[name]
# return s
#
#
# a = get_spam('foo')
# b = get_spam('bar')
# c = get_spam('foo')
# print(a is c)


# class Spam:
# def __init__(self):
# print('__init__')
# raise TypeError('不能实例化对象')
#
# @classmethod
# def _new(cls,name):
# print('__new__')
# self = cls.__new__(cls)
# self.name = name
# return self
#
#
# class CacheSpamManage:
# def __init__(self):
# self._cache = weakref.WeakValueDictionary()
#
# def get_spam(self, name):
# if name not in self._cache:
# s = Spam._new(name)
# self._cache[name] = s
# else:
# s = self._cache[name]
# return s
#
# def clear(self):
# self._cache.clear()
#
#
# s = CacheSpamManage()
# a = s.get_spam('foo')
# b = s.get_spam('bar')
# c = s.get_spam('foo')
# print(a is c)

创建可管理的属性

class Person:
def __init__(self,first_name):
self.first_name = first_name

@property
def first_name(self):
return self._first_name

@first_name.setter
def first_name(self,value):
if not isinstance(value,str):
raise TypeError('参数必须是字符串')
else:
self._first_name = value

p = Person('42')
# p = Person(42)
print(p.__dict__)

#对象实例化的时候会触发 @first_name.setter包装的函数进行自动的类型检测
#如果类中有多个属性需要进行类型检测使用描述符进行检测
#如果类中需要进行类型检测的属性很少可以使用property进行类型检测

# class Person:
# def __init__(self,first_name):
# self.set_first_name(first_name)
#
# def get_first_name(self):
# return self._first_name
#
# def set_first_name(self,value):
# if not isinstance(value,str):
# raise TypeError('参数必须是字符串')
# else:
# self._first_name = value
#
# def del_first_name(self):
# raise AttributeError('不能删除属性')
#
# name = property(get_first_name,set_first_name,del_first_name)
#
# s = Person('42')


创建新的类或实例属性

# class Typed:
#
# def __init__(self, name, expected_type):
# self.name = name
# self.expected_type = expected_type
#
# def __get__(self, instance, owner):
# if instance is None:
# return self
# return instance.__dict__[self.name]
#
# def __set__(self, instance, value):
# if not isinstance(value, self.expected_type):
# raise TypeError('{}参数必须是{}类型'.format(value, self.expected_type.__name__))
# instance.__dict__[self.name] = value
#
# def __delete__(self, instance):
# del instance.__dict__[self.name]
#
#
# def decorate_person(**kwargs):
# def wapper(cls):
# for name, expected_type in kwargs.items():
# setattr(cls, name, Typed(name, expected_type))
# return cls
#
# return wapper
#
#
# @decorate_person(name=str, age=int, gender=str)
# class Person:
#
# def __init__(self, name, age, gender):
# self.name = name
# self.age = age
# self.gender = gender
#
#
# p = Person(123, 18, '男')
# print(p.name)
# print(p.age)
# print(p.gender)

"""
如果你只是想简单的自定义某个类的单个属性访问的话就
不用去写描述器了。这种情况下使用 property 技术会更加容易。
"""


class Person:
def __init__(self,name):
self.name = name

@property
def name(self):
return self._name

@name.setter
def name(self, value):
if not isinstance(value, str):
raise TypeError('{}参数必须是{}类型'.format(value,str.__name__))
self._name = value

# p = Person(123)
#TypeError: 123参数必须是str类型
p = Person('123')


使用延迟计算属性
import math

class lazyproperty:

def __init__(self,func):
self.func = func

def __get__(self, instance, owner):
if instance is None:
return self
else:
value = self.func(instance)
setattr(instance,self.func.__name__,value)
return value


#另一个实现方式没有上一种方法高效但是可以阻止给属性赋值


def lazyproperty(func):
name = '_lazy_' + func.__name__
@property
def lazy(args):

if hasattr(args, name):
return getattr(args, name)
else:
value = func(args)
setattr(args, name, value)
return value
return lazy


class Circle:
def __init__(self,radius):
self.radius = radius

@lazyproperty
def area(self):
print('Computing area')
return math.pi * self.radius ** 2

@lazyproperty
def perimeter(self):
print('Computing perimeter')
return math.pi * 2 * self.radius

c = Circle(4.0)
"""
c.radius
4.0

c.area
Computing area
50.26548245743669

c.area
50.26548245743669

vars(c)
{'radius': 4.0, 'area': 50.26548245743669}

c.perimeter
Computing perimeter
25.132741228718345

vars(c)
{'radius': 4.0, 'area': 50.26548245743669, 'perimeter': 25.132741228718345}

c.perimeter
25.132741228718345
"""


简化数据结构的初始化

import math


class Structure:
_fields = []

def __init__(self, *args, **kwargs):
if len(args) != len(self._fields):
raise TypeError(('Expected {} arguments'.format(len(self._fields))))

for name, value in zip(self._fields, args):
setattr(self, name, value)

expected_field = kwargs.keys() - self._fields

for name in expected_field:
setattr(self, name, kwargs.pop(name))

if kwargs:
raise TypeError('Duplicate values for {}'.format(','.join(kwargs)))


class Stock(Structure):
_fields = ['name','age','gender']

s = Stock('小于',18,'男',date='2017-1-21')

print(vars(s))
#{'name': '小于', 'age': 18, 'gender': '男', 'date': '2017-1-21'}

#如果有很多需要初始化的数据量比较小的类使用这个方法会很方便

定义接口或者抽象基类

from abc import ABCMeta, abstractmethod

class IStream(metaclass=ABCMeta):

@abstractmethod
def read(self, maxbytes = -1):
pass

@abstractmethod
def write(self, data):
pass


class S(IStream):
def __init__(self,a):
self.a = a

def read(self, maxbytes = -1):
pass
def write(self, data):
pass

s = S('a')
#TypeError: Can't instantiate abstract class S with abstract methods read, write
#继承抽象基类定义的类必须实现抽象类中的abstractmethod装饰的方法

@abstractmethod 还能注解静态方法、类方法和 properties 。你只需保证这个注
解紧靠在函数定义前即可:

class A(metaclass=ABCMeta):
  @property
  @abstractmethod
  def name(self):
    pass


  @name.setter
  @abstractmethod
  def name(self, value):
    pass


实现数据模型的类型约束

class Descriptor:

def __init__(self, name=None, **opts):
self.name = name
for key, value in opts.items():
setattr(self, key, value)

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


# class Type(Descriptor):
# expected_type = type(None)
#
# def __set__(self, instance, value):
# if not isinstance(value, self.expected_type):
# raise TypeError('expected' + str(self.expected_type))
# super().__set__(instance, value)


# 实现Type装饰器
def Type(expected_type, cls=None):

if cls is None:
return lambda cls: Type(expected_type, cls)

super_set = cls.__set__

def __set__(self, instance, value):
if not isinstance(value, expected_type):
raise TypeError('expected' + str(expected_type))
super_set(self, instance, value)

cls.__set__ = __set__
return cls


# class Unsigned(Descriptor):
#
# def __set__(self, instance, value):
# if value < 0:
# raise ValueError('expected >= 0')
# super().__set__(instance, value)

# Unsigned装饰器

def Unsigned(cls):
super_set = cls.__set__

def __set__(self, instance, value):
if value < 0:
raise ValueError('expected >= 0')
super_set(self, instance, value)

cls.__set__ = __set__
return cls


#
# class MaxSizes(Descriptor):
#
# def __init__(self, name=None, **opts):
# if 'size' not in opts:
# raise TypeError('missing in opt')
# super().__init__(name, **opts)
#
# def __set__(self, instance, value):
# if len(value) >= self.size:
# raise ValueError('size must be <' + str(self.size))
# super().__set__(instance, value)

# MaxSizes装饰器
def MaxSizes(cls):

super_init = cls.__init__

def __init__(self, name=None, **kwargs):
if 'size' not in kwargs:
raise TypeError('missing in opt')
super_init(self, name, **kwargs)

cls.__init__ = __init__

super_set = cls.__set__

def __set__(self, instance, value):
if len(value) >= self.size:
raise ValueError('size must be <' + str(self.size))
super_set(self, instance, value)

cls.__set__ = __set__
return cls


@Type(int)
class Integer(Descriptor):
pass


@Unsigned
class UnsignedInteger(Integer):
pass


@Type(float)
class Float(Descriptor):
pass


@Unsigned
class UnsignedFloat(Float):
pass


@Type(str)
class Str(Descriptor):
pass


@MaxSizes
class MaxSizesStr(Str):
pass


# 使用装饰器进行类型检测
# def check_attr(**kwargs):
# def decorate(cls):
# for key, value in kwargs.items():
# if isinstance(value, Descriptor):
# value.name = key
# setattr(cls, key, value)
# else:
# setattr(cls, key, value(key))
# return cls
#
# return decorate


# 使用元类进行类型检测
# class MetaType(type):
#
# def __new__(cls, cls_name, base, method):
# for key, value in method.items():
# if isinstance(value, Descriptor):
# value.name = key
#
# return type.__new__(cls, cls_name, base, method)


# @check_attr(name=MaxSizesStr(size=8),shares=UnsignedInteger,price=UnsignedFloat)
class Share():
name = MaxSizesStr('name',size=8)
shares = UnsignedInteger('shares')
price = UnsignedFloat('price')

def __init__(self, name, shares, price):
self.name = name
self.shares = shares
self.price = price


s = Share('阿里', 70, 70.0)
print(vars(s))
"""
这种方式定义的类跟之前的效果一样,而且执行速度会更快。设置一个简单的类型
属性的值,装饰器方式要比之前的混入类的方式几乎快 100%。
"""

posted @ 2019-02-19 14:37  篓子  阅读(197)  评论(0编辑  收藏  举报