# 对象
[TOC]
## 访问控制(私有属性和私有方法)
```python
# 私有属性(变量)
class Student(object):
def __init__(self, name, score):
self.__name = name # 属性的名称前加上两个下划线__,外部就不能访问
self.__score = score
def _show_info(self):
print(self.__name)
print(self.__score)
def print_score(self):
print('%s: %s' % (self.__name, self.__score))
bart = Student('Bart Simpson', 59)
bart.__name # AttributeError: 'Student' object has no attribute '__name'
bart.print_score() # Bart Simpson: 59
bart.__name = 'New Name' # 设置__name变量!
bart.__name # 'New Name'
# 注意: 看上去好像设置私有变量成功,但实际上这个__name变量和class内部的__name变量不是一个变量!内部的__name变量已经被Python解释器自动改成了_Student__name,而外部代码给bart新增了一个__name变量
bart.__show_info() # 私有方法不能访问:AttributeError: 'Student' object has no attribute '__show_info'
```
1. 变量名类似__xxx__的,也就是以双下划线开头,并且以双下划线结尾的,是特殊变量,特殊变量是可以直接访问的,不是private变量
2. 只以一个下划线开头的实例变量名,比如_name,这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽然我可以被访问,但是,请把我视为私有变量,不要随意访问”。
## 类的方法
```python
class Foo:
name = 'who'
count = 0
# __init__也属于实例方法,实例方法必须依托于实例
def __init__(self, name):
self.name = name
# 每实例一次,调用一次, 这里可以统计该类调用的次数
self.__class__.count += 1
print(self.__class__.count)
# 类实例方法:第一个参数强制为类实例对象,一般用self代替
# 类实力为对象后可以直接访问的方法
# 可以通过这个类实例对象(self)访问对象属性
# 可以通过类实例对象(self)的__class__属性访问类属性
def hi(self , str):
print(self.__class__.name) # who
print(str , self.name) # hello zxg
# 类方法:cls即为类自身
@classmethod
def class_method(cls):
print(cls.count)
# 静态方法
# 不能传递和类或实例相关的参数,如cls或self,但可以传递其他参数
@staticmethod
def static_method(nickname):
print(nickname)
if __name__ == '__main__':
foo01 = Foo('zxg')
# foo01.hi('hello')
# Foo.hi('hello') # 报错:实例方法不能通过类直接用,必须依托于实例,当然可以把foo01的对象作为第一参数传进去
foo01.class_method() # 可以通过实例调用
Foo.class_method() # 也可以直接通过类来调用
# foo01.static_method('zxg')
# Foo.static_method('zxg')
# print(foo01)
# print(Foo)
```
1. 逻辑上,类方法应当只被类调用,实例方法实例调用,静态方法两者都能调用
2. 主要区别在于参数传递上的区别,实例方法悄悄传递的是self引用作为参数,而类方法悄悄传递的是cls引用作为参数。而静态方法不会任何隐式传递
## 使用__slots__限制实例的属性
```python
class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
# 使用__slots__要注意,__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的:
class GraduateStudent(Student):
pass
bart = Student() # 对象实例不需要用new
```
## 类和类方法的装饰器
### 类方法中的装饰器
```python
# 原来的class
class Student(object):
def get_score(self):
return self._score
def set_score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
class Student(object):
@property
def score(self):
return self._score
@score.setter
def score(self, value):
if not isinstance(value, int):
raise ValueError('score must be an integer!')
if value < 0 or value > 100:
raise ValueError('score must between 0 ~ 100!')
self._score = value
s = Student()
s.score = 'haha' # OK,实际转化为s.set_score(60)
s.score # OK,实际转化为s.get_score()
# 只定义getter方法,不定义setter方法就是一个只读属性
class Student(object):
@property
def birth(self):
return self._birth
@birth.setter
def birth(self, value):
self._birth = value
@property
def age(self):
return 2018 - self._birth
zxg = Student()
zxg.birth = 1988
zxg.age # 30
Student.sex = 1
zxg.sex = 1 # python一切皆对象,类也属于对象,可以动态为类添加属性,该属性会体现在实例后的对象里面
```
### 类装饰器
#### 函数装饰类
##### 装饰器无参数
```python
#给每个类打印一句话
def Decorator(obj):
print("定义了一个装饰器函数")
return obj
# 相当于执行 Decorator(School)
@Decorator
class School():
pass
def godme(fun):
def __godme(self,message):
print('before')
fun(self,message)
print('after')
return __godme
class Person:
def show(self,message):
print(message)
@godme
def say(self,message):
print(message)
person = Person()
# 相当于 godme(person.say('happy'))
# 调用顺序 godme() -> __godme()-> person.say()
# 输出顺序 before -> happy -> after
person.say('happy')
```
##### 装饰器有参数
```python
#给每个类添加一个可变的数据属性
def Decorator(**kwargs):
def add(obj):
# <class '__main__.School'>
print(obj)
"添加数据属性"
print('调用外部函数的**kwargs',kwargs)
for key,val in kwargs.items():
# 添加数据属性
setattr(obj,key,val)
return obj
print("外部传入的参数为:",kwargs)
return add
# 执行顺序:
# 1.运行Decorator函数,先打印外部的传入的参数,返回add函数名;
# 2.再执行School = add(School)
# Decorator(addr = "浙江省杭州市",name ="浙江大学")(School)
@Decorator(addr = "浙江省杭州市",name ="浙江大学")
class School():
def __init__(self,price):
self.price =price
```
#### 类装饰函数或方法
##### 无参数
```python
class tracer:
def __init__(self,func):
print('__init__ is called' )
self.calls = 0
self.func = func
def __call__(self,*args):
self.calls += 1
print(args)
print('call %s to %s' %(self.calls, self.func.__name__))
self.func(*args)
@tracer
def spam(a, b, c):
print(a + b + c)
'''
1. 相当于 tracer(spam(1,2,3))
2. 调用顺序:__init__(spam) -> __call__() -> spam(a, b, c)
'''
spam(1,2,3)
```
##### 有参数
```python
class tracer:
def __init__(self, *args):
self.calls = 0
self.args = args
print(args)
def __call__(self, func):
self.func = func
def realfunc(*args):
self.calls += 1
print('call %s to %s' %(self.calls, self.func.__name__))
self.func(*args)
return realfunc
# 相当于tracer("xxxx")(spam)
# 调用顺序 __init__("xxxx") -> __call__(spam) -> realfunc(a, b, c) -> spam(a, b, c)
@tracer("xxxx")
def spam(a, b, c):
print(a + b + c)
spam(1,2,3)
```
## 继承
```python
class Parent:
def myMethod(self):
print ('调用父类方法')
class Child(Parent):
def myMethod(self):
# super().myMethod(); # Python3,如果super在类里面的话,可以省略里面的参数
print ('调用子类方法')
c = Child() # 子类实例
c.myMethod() # 子类调用重写方法
super(Child,c).myMethod() #用子类对象调用父类已被覆盖的方法
```
## 多重继承
在设计类的继承关系时,通常,主线都是单一继承下来的,如果需要“混入”额外的功能,通过多重继承就可以实现。
```python
class Dog(Mammal, RunnableMixIn, CarnivorousMixIn):
pass
```
## 定制类(魔术方法)
类似PHP中的魔术方法
```python
# __str__ # 打印实例时调用
# __repr__ # console直接敲命令时调用
# object python的顶级父类
class Student(object):
def __init__(self, name):
self.name = name
# print打印时触发
def __str__(self):
return 'Student object (name: %s)' % self.name
# 控制台输出时触发
__repr__ = __str__ # 在Python console直接敲命令时调用__repr__方法,又因为__repr__方法跟__str__的内容一样
zxg = Student('zxg')
print(zxg) # Student object (name: zxg)
# __iter__ 实现迭代
# __getitem__ list化之后取第几个元素及切片
class Fib(object):
def __init__(self):
self.a, self.b = 0, 1 # 初始化两个计数器a,b
def __iter__(self):
return self # 实例本身就是迭代对象,故返回自己
# 循环时触发
def __next__(self):
self.a, self.b = self.b, self.a + self.b # 计算下一个值
if self.a > 100: # 退出循环的条件
raise StopIteration()
return self.a # 返回下一个值
# list化之后切片触发
def __getitem__(self, n):
print(n)
if isinstance(n, int): # n是索引
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a
if isinstance(n, slice): # n是切片
start = n.start
stop = n.stop
if start is None:
start = 0
a, b = 1, 1
L = []
for x in range(stop):
if x >= start:
L.append(a)
a, b = b, a + b
return L
f = Fib()
f[5] # 5
f[:5] # slice(1, 5, None)
for n in f:
print(n)
# __setitem__() # 设置元素时触发
# __delitem__() # 删除元素时触发
# 通过以上的几个魔术方法,可以把自己定义的类表现出list,dict,tuple的特性
# __getattr__ 获取不存在的元素时调用
class Chain(object):
def __init__(self, path=''):
self._path = path
def __getattr__(self, path):
return Chain('%s/%s' % (self._path, path))
def __str__(self):
return self._path
__repr__ = __str__
Chain().status.user.list # /status/user/list 实现链式操作
# __call__ # 对实例直接调用
class Student(object):
def __init__(self, name):
self.name = name
# 对实例直接调用的时候,调用的就是这个方法
def __call__(self):
print('My name is %s.' % self.name)
s = Student('Michael')
s() # 直接可以调用
# __new__ 当你继承一些不可变的class时(比如int, str, tuple), 提供给你一个自定义这些类的实例化过程的途径。还有就是实现自定义的metaclass。
```
## 待处理
__del__
__enter__
__exit__
## 使用枚举类
```python
from enum import Enum
Month = Enum('Month', ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'))
print(Month) # <enum 'Month'>
for name, member in Month.__members__.items():
# Sep => Month.Sep , 9
print(name, '=>', member, ',', member.value)
```
## __new__ 和 __init__
> **实际上在我们实例化对象时,python默认执行了__new__操作,先创建类实例,然后返回给__init__初始化实例**
```python
# python新类允许用户重载__new__和__init__方法。__new__创建类实例,__init__初始化一个已被创建的实例;
class newStyleClass(object):
# __new__在顶级父类object是一个静态方法
# cls代表当前类
def __new__(cls):
print("__new__ is called")
return super(newStyleClass, cls).__new__(cls)
# self
def __init__(self):
print("__init__ is called")
print("self is: ", self)
newStyleClass()
# 我们发现__new__函数先被调用,接着__init__函数在__new__函数返回一个实例的时候被调用,并且这个实例作为self参数被传入了__init__函数
# 若__new__()没有正确返回当前类cls的实例,那__init__()将不会被调用,即使是父类的实例也不行。
obj = 12
# obj can be an object from any class, even object.__new__(object)
class returnExistedObj(object):
def __new__(cls):
print("__new__ is called")
return obj # __init__不被调用
# return super(returnExistedObj,cls).__new__(cls)
def __init__(self):
print("__init__ is called")
print("self is: ", self)
returnExistedObj()
```
## 使用元类
### 通过type函数临时创建类
Python函数和类的定义,不是编译时定义的,而是运行时动态创建的;所以可以在运行时临时动态的创建类
`type`传入三个参数:
1. class的名称;
1. 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
1. class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。这里的dict的参数是**kws;
```python
def fn(self, name='world'): # 先定义函数
print('Hello, %s.' % name)
Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class
o = Hello()
o.hello()
```
### metaclass
除了使用type()动态创建类以外,要控制类的创建行为,还可以使用metaclass。先定义metaclass,就可以创建类,最后创建实例。
metaclass是Python面向对象里最难理解,也是最难使用的魔术代码。正常情况下,你不会碰到需要使用metaclass的情况
```python
# metaclass是类的模板(类的类),所以必须从`type`类型派生:
# metaclass的类名总是以Metaclass结尾
class ListMetaclass(type):
def __new__(cls, name, bases, attrs):
attrs['add'] = lambda self, value: self.append(value)
return type.__new__(cls, name, bases, attrs)
# 有了ListMetaclass,我们在定义类的时候还要指示使用ListMetaclass来定制类,传入关键字参数metaclass
# 当我们传入关键字参数metaclass时,魔术就生效了,它指示Python解释器在创建MyList时,要通过ListMetaclass.__new__()来创建,在此,我们可以修改类的定义,比如,加上新的方法,然后,返回修改后的定义。
class MyList(list, metaclass=ListMetaclass):
pass
L = MyList()
L.add(1)
print(L)
```
**元类的作用**
1. 拦截类的创建;
2. 修改类;
3. 返回修改之后的类;
```python
# 看下面这个例子
# type其实也是个元类
class UpperAttrMetaclass(type):
def __new__(cls, clsname, bases, dct):
uppercase_attr = {}
for name, val in dct.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val
return super(UpperAttrMetaclass, cls).__new__(cls, clsname, bases, uppercase_attr)
# 我们知道在python中一切皆对象
# 那么下面我们的s其实就是一个类对象,它的__name__其实和Foo的__name__是一样的
# 看下面的方法是不是和type创建类有点像?
s = UpperAttrMetaclass('Foo',(object,),{'bar':'bip'})
print(hasattr(s,'bar')) # False
print(hasattr(s,'BAR')) # True
print(s.BAR) # bip
class Foo(object,metaclass=UpperAttrMetaclass):
bar='bip'
print(hasattr(Foo,'bar'))
print(hasattr(Foo,'BAR'))
print(Foo.BAR)
# 最后它们输出的结果其实是一模一样的,这就说明了类其实和我们普通的实例对象差不多,只不过普通实例是通过类创建,而类是通过元类创建
# 而__new__就是用来创建实例的,不论是普通的实例,还是类实例,总之就是个实例
```
**意义**
实现面向对象高级编程,体现其“开闭原则”:把扩展打开,把修改关闭。
比如: 编写一个ORM框架,所有的类都只能动态定义,因为只有使用者才能根据表的结构定义出对应的类来。
```python
class Field(object):
def __init__(self, name, column_type):
self.name = name # 字段名
self.column_type = column_type # 字段类型
def __str__(self):
return '<%s: %s>' % (self.__class__.__name__, self.name)
class StringField(Field):
def __init__(self, name):
# super().__init__(name, 'varchar(100)') # Python3里面的super可以省略StringField, self
super(StringField, self).__init__(name, 'varchar(100)') # 字段名和字段类型
class IntegerField(Field):
def __init__(self, name):
super(IntegerField, self).__init__(name, 'bigint') # 字段名和字段类型
class ModelMetaclass(type):
def __new__(cls, name, bases, attrs): # cls: 正在被生成的对象, name: 生成该对象的类名, bases: 父类集合, attrs: 属性方法集合
if name == 'Model': # 若类名为Model, 则是对Model的修改,这里是原样返回
return type.__new__(cls, name, bases, attrs)
print('Found model: %s' % name) # 输出类的名字
mappings = dict() # 初始化user类的属性方法字典
for key, value in attrs.items():
if isinstance(value, Field):
print('Found mappings: %s ==> %s' % (key, value)) # 输出属性类型和属性名
mappings[key] = value # 将属性类型(key)和属性名(value)加入到字典
for key in mappings.keys():
attrs.pop(key) # 类属性中删除该Field属性, 否则容易造成运行时错误(实例的属性会遮盖类的同名属性)
attrs['__mappings__'] = mappings # 将属性方法字典加入到属性方法集合中
attrs['__table__'] = name # 表名(此处假设类名即为表名)
return type.__new__(cls, name, bases, attrs)
class Model(dict, metaclass=ModelMetaclass): # 使用ModelMetaclass来创建类
def __init__(self, **kw): # **kw命名关键词参数
print(kw)
super(Model, self).__init__(**kw)
def __getattr__(self, key): # 获得values
try:
return self[key]
except KeyError:
raise AttributeError(r"'Model' object has no attribute '%s'" % key)
def __setattr__(self, key, value): # 设置属性/方法名
self[key] = value
def save(self): # 将属性保存到数据库(此处仅为生成相应的SQL语句)
fields = []
params = []
args = []
for k, v in self.__mappings__.items():
fields.append(v.name) # 将字段名加入到fields中
params.append('?') # 向params中添加xxx个问号
args.append(getattr(self, k, None)) # 将values加入args中
sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(params)) # 生成SQL语句, join方法是将list中的内容以前面的符号作为分隔连在一起
print('SQL: %s' % sql)
print('ARGS: %s' % args)
class User(Model):
# 定义类的属性到列的映射:
id = IntegerField('id')
name = StringField('username')
email = StringField('email')
password = StringField('password')
# 创建一个实例:
u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd')
# 保存到数据库:
u.save()
# 过程是
'''
从上到下执行:
Model => ModelMetaclass => User(以及里面的属性IntegerField,StringField等) => ModelMetaclass => Model.save
'''
```
### `__metaclass__`
python2继承元类的语法
```python
# python3
class Model(dict, metaclass=ModelMetaclass):
pass
# python2
class Model(dict):
__metaclass__ = ModelMetaclass
pass
```