类以及OOP面向对象


对象的比较 is==


a = [1, 2, 3]
b = a


a == b
True
a is b
True


c = list(a)

a == c
True

a is c
False


上面的 a, c 指向不同的list对象, 但是他们的内容是相同的

is 如果两个变量指向相同的对象

== 两个变量内容相等

• An is expression evaluates to True if two variables point to the
same (identical) object.

• An == expression evaluates to True if the objects referred to by
the variables are equal (have the same contents).

每个类需要一个 __repr__

  • 使用__repr__, __str__ 方法 转换 对象成字符串

class Car:
    def __init__(self, color, mileage):
        self.color = color
        self.mileage = mileage
    def __str__(self):
        return f'a {self.color} car'

my_car = Car('red', 37281)

print(my_car)
'a red car'

  • 什么时候会转换(调用)
# 1
print(my_car)
a red car

#2
str(my_car)
'a red car'


#3
'{}'.format(my_car)
'a red car'

  • __str____repr__的区别

如果没有 __str__ python 会自动使用 __repr__的结果


class Car:
    def __init__(self, color, mileage):
        self.color = color
        self.mileage = mileage
    def __repr__(self):
        return '__repr__ for Car'
    def __str__(self):
        return '__str__ for Car'

__repr__ 是展现给python解释器的 __str__ 是展现给用户的

我们可以看一下 datetime模块中的使用方式


import datetime
today = datetime.date.today()

str(today)
'2017-02-02'


repr(today)
'datetime.date(2017, 2, 2)'
# 会带上 模块名

  • 怎么样写一个 __repr__方法

class Test:
    def __init__(self, name, color):
        self.name = name
        self.color = color

    def __repr__(self):
        # Don’t Repeat Yourself (DRY) 规则
        return (
                  f'{self.__class__.__name__}'
                  f'({self.name!r}, {self.color!r})'
                )
                
    def __str__(self):
        return f'a {self.color} car'

  • python2中的 __unicode__

py2中 默认 __str__返回 bytes, 而__unicode__返回字符串

以下是py2 字符串方法定义的一个完整方法


class Car(object):
    def __init__(self, color, mileage):
        self.color = color
        self.mileage = mileage
    def __repr__(self):
        return '{}({!r}, {!r})'.format(
        self.__class__.__name__,
        self.color, self.mileage)
    def __unicode__(self):
        return u'a {self.color} car'.format(
        self=self)
    def __str__(self):
        return unicode(self).encode('utf-8')

定义一个自己的异常类

更好地定位,以及处理异常


def validate(name):
    if len(name) < 10:
        raise ValueError


class NameTooShortError(ValueError):
    pass
    
def validate(name):
    if len(name) < 10:
    raise NameTooShortError(name)

如果是一个会被调用的模块, 异常应该细化


class BaseValidationError(ValueError):
    pass


class NameTooShortError(BaseValidationError):
    pass
class NameTooLongError(BaseValidationError):
    pass
class NameTooCuteError(BaseValidationError):
    pass
    

# 方便使用 细化处理
try:
    validate(name)
except BaseValidationError as err:
    handle_validation_error(err)

对象的复制, 拷贝

对于不可变对象, python中的赋值语句不会创建新的对象, 只是把变量指向这个对象

一个浅拷贝的例子 shallow copy


xs = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
ys = list(xs) # shallow copy

xs.append(['new sublist'])
# ys 浅拷贝, 最外层的列表 list 到另一个内存地址, 
xs
[[1, 2, 3], [4, 5, 6], [7, 8, 9], ['new sublist']]
ys
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]



xs[1][0] = 'X'
# ys 浅拷贝  但是列表内部的元素只是拷贝的引用, 还是指向的原地址

 xs
[[1, 2, 3], ['X', 5, 6], [7, 8, 9], ['new sublist']]
ys
[[1, 2, 3], ['X', 5, 6], [7, 8, 9]]

深拷贝 deep copy

import copy

xs = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
zs = copy.deepcopy(xs)

xs
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
zs
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]


xs[1][0] = 'X'

xs
[[1, 2, 3], ['X', 5, 6], [7, 8, 9]]
zs
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]

抽象类 Abstract Base Classes (ABCs) 继承指定的方法

Abstract Base Classes 可以防止忘记实现子类的特定接口, 及时抛出错误

  • 没有抽象类, 实现方式

class Base:
    def foo(self):
        raise NotImplementedError()
        
    def bar(self):
        raise NotImplementedError()

class Concrete(Base):
    def foo(self):
        return 'foo() called'
    # Oh no, we forgot to override bar()...
    # def bar(self):
    #   return "bar() called"
    

>>> c = Concrete()
>>> c.foo()
'foo() called'
>>> c.bar()
NotImplementedError
  • 使用 abc模块, py2.6+

from abc import ABCMeta, abstractmethod

class Base(metaclass=ABCMeta):
    @abstractmethod
    def foo(self):
        pass
    @abstractmethod
    def bar(self):
        pass
    
class Concrete(Base):
    def foo(self):
        pass
    # We forget to declare bar() again...


> c = Concrete()
TypeError:
"Can't instantiate abstract class Concrete
with abstract methods bar"

没有 abc 模块 , 没有实现指定的父类接口, 我们只是获得一个 a NotImplementedError

有名分组 Namedtuples 的用处 py2.6+

是一种手动`定义 类` 的方式

是 python 内置的 tuple的扩展

是不可变的

Namedtuples are a memory-efficient shortcut to defining an immutable class in Python manually

from collections import namedtuple
Car = namedtuple('Car' , 'color mileage')
# Car = namedtuple('Car', ['color', 'mileage']) 
  • Namedtuple是类, 可以继承
Car = namedtuple('Car', 'color mileage')

class MyCarWithMethods(Car):
    def hexcolor(self):
        if self.color == 'red':
            return '#ff0000'
        else:
            return '#000000'
  • namedtuple 的一些属性,方法
from collections import namedtuple

Car = namedtuple('Car', 'color mileage')
car = Car('red', 2456)

_fields


Car._fields
('color', 'mileage')


ElectricCar = namedtuple(
   'ElectricCar', Car._fields + ('charge',))

_asdict()

my_car._asdict()
OrderedDict([('color', 'red'), ('mileage', 3812.4)])

json.dumps(my_car._asdict())
'{"color": "red", "mileage": 3812.4}'

_replace()

创建一个新的对象

new_car = my_car._replace(color='blue')
Car(color='blue', mileage=3812.4)

_make() 创建一个新的对象


Car._make(['red', 999])
Car(color='red', mileage=999)

类与对象的 变量问题

类变量 和 对象变量


class Dog:
    num_legs = 4 # <- Class variable
    
    def __init__(self, name):
        self.name = name # <- Instance variable

类变量的变化会影响全部的实例对象


class CountedObject:
    num_instances = 0
    def __init__(self):
        self.num_instances += 1

print(CountedObject.num_instances)
print(CountedObject().num_instances)
print(CountedObject().num_instances)
print(CountedObject.num_instances)

# 0
# 1
# 1
# 0



class CountedObject:
    num_instances = 0
    def __init__(self):
        self.__class__.num_instances += 1

print(CountedObject.num_instances)
print(CountedObject().num_instances)
print(CountedObject().num_instances)
print(CountedObject.num_instances)

# 0
# 1
# 2
# 2

Buy me a 肥仔水!