python类的各种属性和方法,以及链式调用

Python 3.6.0 (v3.6.0:41df79263a11, Dec 23 2016, 07:18:10) [MSC v.1900 32 bit (Intel)] on win32
Type “copyright”, “credits” or “license()” for more information.

#仅仅把属性和方法列出来是不够的,配合 getattr()、setattr()以及hasattr(),我们可以直接操作一个对象的状态:
class MyObject(object):
pass

shili = MyObject()
hasattr(shili, ‘x’) # 有属性’x’吗?
False

shili.x#属性是什么(值)
Traceback (most recent call last):
File “<pyshell#6>”, line 1, in
shili.x#属性是什么(值)
AttributeError: ‘MyObject’ object has no attribute ‘x’

setattr(shili, ‘y’, 19) # 设置一个属性’y’
hasattr(shili, ‘y’) # 有属性’y’吗?
True

getattr(shili, ‘y’) # 获取属性’y’
19

shili.y
19

#obj.y 是调用方法getattr得到getattr(shili, ‘y’)值
getattr(shili, ‘z’, 404) # 获取属性’z’,如果不存在,返回默认值 404
404

hasattr(shili, ‘power’) # 有属性’power’吗?
False

#给实例绑定属性的方法是通过实例变量bart.xxx,或者通过 self 变量:

#如果 Student 类本身需要绑定一个属性呢?可以直接在 class 中定义属性是类属性
class Student(object):
name = ‘Student’

#类的所有实例都可以访问到
s = Student()
s.name
‘Student’

Student.name
‘Student’

s.name = ‘Michael’ # 给实例绑定 name 属性
s.name
‘Michael’

#屏蔽掉类的name 属性
Student.name
‘Student’

del s.name # 如果删除实例的 name 属性
s.name
‘Student’

#使用__slots__

给实例绑定一个方法

def set_age(self, age): # 定义一个函数作为实例方法
self.age = age

from types import MethodType
s.set_age = MethodType(set_age, s) # 给实例绑定一个方法
s.set_age(25) # 调用实例方法
s.age # 测试结果
25

#为了给所有实例都绑定方法,可以给 class 绑定方法:
def set_score(self, score):
self.score = score

Student.set_score = MethodType(set_score, Student)
#给 class 绑定方法后,所有实例均可调用
s.set_score(100)
s.score
100

s2 = Student()
s2.set_score(99)
s2.score
99

#使用__slots_
#定义 class 的时候,定义一个特殊的__slots__变量,来限制该 class 实例能添加的属性
class Student(object):
slots = (‘name’, ‘age’) # 用 tuple 定义允许绑定的属性名称

s = Student() # 创建新的实例
s.name = ‘Michael’ # 绑定属性’name’
s.age = 25 # 绑定属性’age
s.score = 99 # 绑定属性’score’
Traceback (most recent call last):
File “<pyshell#73>”, line 1, in
s.score = 99 # 绑定属性’score’
AttributeError: ‘Student’ object has no attribute ‘score’

#__slots__定义的属性仅对当前类实例起作用,对继承的子类是不起作用的:
class GraduateStudent(Student):
pass

g = GraduateStudent()
g.score = 9999
g.score
9999

#使用@property
#在绑定属性时,没办法检查参数,导致可以把成绩随便改

set_score()方法来设置成绩,get_score()来获取成绩

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

#Python 内置的@property 装饰器就是负责把一个方法变成属性调用的:

class Student(object):
@property ##!!!
def get_score(self):
return self._score
@score.setter
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

Traceback (most recent call last):
File “<pyshell#100>”, line 1, in
class Student(object):
File “<pyshell#100>”, line 5, in Student
@score.setter
NameError: name ‘score’ is not defined

class Student(object):
@property ##!!!
def Tscore(self):###改成同一个名称 不用上门的一个get_和set__的两个名称
return self._score
@Tscore.setter
def Tscore(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

#把一个 getter 方法变成属性,只需要加上@property 就可以了
#此时,@property 本身又创建了另一个装饰器@score.setter把一个 setter 方法变成属性赋值
s = Student()
s.Tscore = 60

OK,实际转化为 s.set_score(60)

s.Tscore
60

s.Tscore = 9999
Traceback (most recent call last):
File “<pyshell#109>”, line 1, in
s.Tscore = 9999
File “<pyshell#102>”, line 10, in Tscore
raise ValueError(‘score must between 0 ~ 100!’)
ValueError: score must between 0 ~ 100!

#多重继承。
class Animal(object):
pass

class Mammal(Animal):
pass

class Dog(Mammal):
pass

class Runnable(object)
SyntaxError: invalid syntax

class Runnable(object):
pass

class Runnable(object):
def run(self):
print(‘Running…’)

class Flyable(object):
def fly(self):
print(‘Flying…’)

class Dog(Mammal, Runnable):
pass

#同时继承e。这种设计通常称之为 MixIn。
class Dog(Mammal, RunnableMixIn, CarnivorousMixIn):
pass

Traceback (most recent call last):
File “<pyshell#147>”, line 1, in
class Dog(Mammal, RunnableMixIn, CarnivorousMixIn):
NameError: name ‘RunnableMixIn’ is not defined

#定制类
print(Student(‘Michael’))
Traceback (most recent call last):
File “<pyshell#156>”, line 1, in
print(Student(‘Michael’))
TypeError: object() takes no parameters

class Student(object):
def init(self, name):
self.name = name

print(Student(‘Michael’))
<main.Student object at 0x03572D30>

#创建一个实例,打印出一堆,不好看
class Student(object):
def init(self, name):
self.name = name
def str(self):####定制类。定制表现形式
return ‘Student object (name: %s)’ % self.name

print(Student(‘Michael’))
Student object (name: Michael)

#细心的朋友会发现直接敲变量不用 print,打印出来的实例还是不好看:
s = Student(‘Michael’)
s
<main.Student object at 0x03572D30>

#这是因为直接显示变量调用的不是__str__(),而是__repr__()
#str()返回用户看到的字符串,print可以调用
s.str

SyntaxError: unexpected indent

s.str
<bound method Student.str of <main.Student object at 0x03572D30>>

s.str()
‘Student object (name: Michael)’

Student(‘Michael’).str()
‘Student object (name: Michael)’

#解决办法是再定义一个__repr__()。但是通常__str__()和__repr__()代码都是一样的
class Student(object):
def init(self, name):
self.name = name
def str(self):####定制类。定制表现形式
return ‘Student object (name: %s)’ % self.name
repr = str

s = Student(‘Michael’)
s
Student object (name: Michael)

#定制类__iter__,该方法返回一个迭代对象
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 > 100000: # 退出循环的条件
raise StopIteration();
return self.a # 返回下一个值

for n in Fib():
print(n)

1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
6765
10946
17711
28657
46368
75025

#getitem
Fib()[5]
Traceback (most recent call last):
File “<pyshell#192>”, line 1, in
Fib()[5]
TypeError: ‘Fib’ object does not support indexing

#把它当成list 来使用还是不行,需要实现__getitem__()方法
class ffib(object):
def getitem(self, n):
a, b = 1, 1
for x in range(n):
a, b = b, a + b
return a

f = ffib()
f[0]
1

#但是 list 有个神奇的切片方法:
#对于 ffib 却报错。原因是__getitem__()传入的参数可能是一个 int,也可能是一个切片对象 slice,所以要做判断:
class ffib(object):
def getitem(self, 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

g=ffib()
f[0:5]
Traceback (most recent call last):
File “<pyshell#203>”, line 1, in
f[0:5]
File “<pyshell#195>”, line 4, in getitem
for x in range(n):
TypeError: ‘slice’ object cannot be interpreted as an integer

g[0:5]
[1]

ffib()[5:10]
[]

ffib()[:10]
[1]

class ffib(object):

def __getitem__(self, 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

g=ffib()
g[0:5]
[1, 1, 2, 3, 5]

#上面是 getitem__实现迭代,
#Python 还有另一个机制,那就是写一个__getattr
()方法,动态返回一个属性
#getattr
class Student(object):
def init(self):
self.name = ‘Michael’
def getattr(self, attr):
if attr==‘score’:
return 99

s = Student()
s.score
99

#返回函数也是完全可以的:
class Student(object):
def getattr(self, attr):
if attr==‘age’:
return lambda: 25

#只是调用方式要变为:
s = Student()
s.age()
25

#!!注意,只有在没有找到属性的情况下,才调用__getattr__,已有的属性,比如 name,不会在__getattr__中查找。
s.abc
s.abc()
Traceback (most recent call last):
File “<pyshell#242>”, line 1, in
s.abc()
TypeError: ‘NoneType’ object is not callable

#注意到任意调用如 s.abc 都会返回 None,这是因为我们定义的__getattr__默认返回就是 None。
class Student(object):
def getattr(self, attr):
if attr==‘age’:
return lambda: 25
raise AttributeError(’‘Student’ object has no attribute’%s’’ % attr)

s = Student()
s.scor
Traceback (most recent call last):
File “<pyshell#247>”, line 1, in
s.scor
File “<pyshell#245>”, line 5, in getattr
raise AttributeError(’‘Student’ object has no attribute’%s’’ % attr)
AttributeError: ‘Student’ object has no attribute’scor’

#利用完全动态的__getattr__,我们可以写出一个链式调用
class Chain(object):
def init(self, path=’’):
self._path = path
print (path)
def getattr(self, path):
return Chain(’%s/%s’ % (self._path, path))
def str(self):
return self._path
repr = str#为了创建实例就能返回结果而不是返回方法特性

Chain().status.user.timeline.list

/status
/status/user
/status/user/timeline
/status/user/timeline/list
/status/user/timeline/list

#’‘并不是空格的意思
#’‘是省略不做动作
#所以第一行是空行!
class Chain(object):
def init(self, path=’’):
self._path = path

def __getattr__(self, path):
	print (path)
	return Chain('%s/%s' % (self._path, path))

def __str__(self):
	return self._path
__repr__ = __str__#为了创建实例就能返回结果而不是返回方法特性

Chain().status.user.timeline.list
status
user
timeline
list
/status/user/timeline/list

#__getattr__动态返回一个属性:任意调用,_getattr__默认返回就是 None

class liejie(object):
def init(self,path=’’):
self.__path=path
print(’%s/%s’ % (self._path, path))

liejie().status
Traceback (most recent call last):
File “<pyshell#265>”, line 1, in
liejie().status
File “<pyshell#264>”, line 4, in init
print(’%s/%s’ % (self._path, path))
AttributeError: ‘liejie’ object has no attribute ‘_path’

#发现没有这个attr(即status)
#采用_getattr__方法来让他可以任意调用

class liejie(object):
def init(self,path=’’):
self.__path=path

def __getattr__(self, path):
	return liejie('%s/%s' % (self._path, path))#调用自身初始化

liejie().status
Traceback (most recent call last):
File “<pyshell#275>”, line 1, in
liejie().status
File “<pyshell#274>”, line 6, in getattr
return liejie(’%s/%s’ % (self._path, path))#调用自身初始化
File “<pyshell#274>”, line 6, in getattr
return liejie(’%s/%s’ % (self._path, path))#调用自身初始化
File “<pyshell#274>”, line 6, in getattr
return liejie(’%s/%s’ % (self._path, path))#调用自身初始化
[Previous line repeated 327 more times]
RecursionError: maximum recursion depth exceeded while calling a Python object

class liejie(object):
def init(self,path=’’):
self._path=path#多了一横,为啥不可以?

def __getattr__(self, path):
	return liejie('%s/%s' % (self._path, path))#调用自身初始化

liejie().status
<main.liejie object at 0x00552830>

liejie().a
<main.liejie object at 0x005527D0>

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

liejie().status
<main.liejie object at 0x00552810>

liejie().status()
Traceback (most recent call last):
File “<pyshell#285>”, line 1, in
liejie().status()
TypeError: ‘liejie’ object is not callable

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__#为了创建实例就能返回结果而不是返回方法特性

liejie().status
<main.liejie object at 0x005528B0>

Chain().status
/status

##当调用不存在的属性时,比如 status,Python 解释器会试图调用__getattr__(self, ‘status’)来尝试获得属性
#只有在没有找到属性的情况下,才调用__getattr__,
#注意到任意调用如 s.abc 都会返回 None,__getattr__默认返回就是 None。但是上面有return自己重复的调用自己

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值