0%

python面向对象笔记

初始化

定义了__init__后就要传入其定义的所有参数。__init__中所定义的属性直接与实例想联。

1
2
3
4
5
6
7
8
9
class Student(object):
def __init__(self,name,score):
self.name = name
self.score = score
def print_score(self):
if self.score>90:
print('A')
else:
print('B')

访问限制

  • 以双划线__开头,__结尾的变量是特殊变量,特殊变量可以直接访问。
  • 以一个下划线_开头的变量是保护变量,只允许其自身和子类使用,不能用form xx import *
  • 以两个下划线__开头的变量是private变量,虽然可以访问,但是按照惯例,应该视为私有变量,只允许这个类访问,不应该访问。
  • 如果要访问和更改内部私有变量,可以在类中设置相应的set_itemchange_itemget_item方法来获得。
1
2
3
4
5
6
7
8
9
10
11
12
13
class Student(object):
def __init__(self,name,score):
self.name = name
self.score = score
self.grade = None
def _score(self):
if self.score > 90:
self.grade = 'A'
else:
self.grade = '-A'
return self.grade
def get_grade(self):
return self._score()

实例属性和类属性

  • 实例属性属于各个实例所有,互不干扰;
  • 类属性属于类所有,所有实例共享一个属性;
  • 不要对实例属性和类属性使用相同的名字,否则将产生难以发现的错误!实例属性的值会覆盖类属性的值
1
2
3
4
# 添加类属性
class Animal(object):
name = 'animal'
pass

继承和多态

  • 继承就是说,我创建一个新的类别的时候,object处填写已有的一个类别,新类别是子类别,旧类别是父类别。
  • 子类别会继承旧类别的所有的属性和方法;
  • 子类别可以更改父类别的方法(会覆盖父类别的方法);
  • 子类别可以添加新的方法
1
2
3
4
5
6
7
8
9
class Animal(object):
def run(self):
print('Animal is running...')

class Dog(Animal):
def run(self):
print('Dog is running...')
def eat(self):
print('Dog eats bones.')

装饰器

简单来说,装饰器就是把另外一个函数当成参数传入到当前函数中并返回一个函数,这个函数会取代被装饰的函数。

装饰器的运行过程

先将被修饰函数放入修饰器中的函数,然后正常运行修饰器函数,当调用被修饰函数时,执行被修饰函数。如果需要传入的被修饰函数需要传参,那么在调用的函数中也要设置参数位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# It’s not black magic, you just have to let the wrapper 
# pass the argument:

def a_decorator_passing_arguments(function_to_decorate):
def a_wrapper_accepting_arguments(arg1, arg2):
print("I got args! Look: {0}, {1}".format(arg1, arg2))
function_to_decorate(arg1, arg2)
return a_wrapper_accepting_arguments

# Since when you are calling the function returned by the decorator, you are
# calling the wrapper, passing arguments to the wrapper will let it pass them to
# the decorated function

@a_decorator_passing_arguments
def print_full_name(first_name, last_name):
print("My name is {0} {1}".format(first_name, last_name))

print_full_name("Peter", "Venkman")
# outputs:
#I got args! Look: Peter Venkman
#My name is Peter Venkman

如果是在类中使用呢?方法和函数的唯一区别只在于第一个参数是self。因此,我们只需要更改一下这个地方即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def method_friendly_decorator(method_to_decorate):
def wrapper(self, lie):
lie = lie - 3 # very friendly, decrease age even more :-)
return method_to_decorate(self, lie)
return wrapper


class Lucy(object):

def __init__(self):
self.age = 32

@method_friendly_decorator
def sayYourAge(self, lie):
print("I am {0}, what did you think?".format(self.age + lie))

l = Lucy()
l.sayYourAge(-3)
#outputs: I am 26, what did you think?

装饰器函数可以不带参数,也可以带参数。当装饰器函数带参数时,其实充当的是装饰器函数的构造器的功能,其携带的参数都可访问,但是不能被传入到「真正」的装饰器函数中,否则会起冲突!在定义装饰器时传入参数,我们可以更好地控制装饰器的行为。但是在构建简单的装饰器时,使用不带参数的即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def methord_with_args(arg1):
print('here is my arg {}'.format(arg1))
def get_fn(fn):
print('run get(fn)')
def wrapper(arg2,arg3,arg4):
print('here is my decorator args: {0},{1},{2}'.format(arg2,arg3,arg4))
fn(arg1,arg3)
return wrapper
return get_fn

@methord_with_args('hi')
def what(arg2,arg3):
print('what',arg2,arg3)

what('2','3','4')
# what 函数传入三个参数而非两个,是因为它已经被装饰后的 wrapper 函数所取代了!

## output:
# here is my arg hi
# run get(fn)
# here is my decorator args: 2,3,4
# what hi 3

常见的内置装饰器函数

类方法@classmethod

在类中定义方法时使用@classmethod装饰器可以不创建实例就被使用。被类方法装饰器装饰的函数虽然不用传入self参数,但是仍需要传入一个cls参数来表示其自身。这个clsself一样是不能传变量的。

静态方法@staticmethod

静态方法可以不创建实例来调用类中的一个方法。静态方法不需要传入参数,但也可以传入参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Myclass:
def method(self):
return 'instance method called',self

@staticmethod
def staticmethod():
return 'staticmethod called'

@classmethod
def classmethod(cls,a):
a += 1
return 'classmethod called',a

print(Myclass.staticmethod())
print(Myclass.classmethod(1))

## output
# staticmethod called
# ('classmethod called', 2)

@property

用途1:与私有变量配合使用,来定义只读属性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Person():
def __init__(self,name):
self.name = name
self._age = 20

@property
def age(self):
return self._age

a = Person('张三')
a.age
# 20

a.age = 30
# 会报错:AttributeError: can't set attribute
用途2:对类中的属性进行检查

原来,我们要对类中属性的设置进行检查,会这样写:

1
2
3
4
5
6
7
8
9
10
11
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

然后调用的时候会这样:

1
2
3
4
5
6
7
8
>>> s = Student()
>>> s.set_score(60) # ok!
>>> s.get_score()
60
>>> s.set_score(9999)
Traceback (most recent call last):
...
ValueError: score must between 0 ~ 100!

但这样用两个方法太麻烦了,有了@property这个装饰器,就会方便很多。我们把一个getter方法变成属性,只需要加上@property就可以了,此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值,于是,我们就拥有一个可控的属性操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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 = 60 # OK,实际转化为s.set_score(60)
>>> s.score # OK,实际转化为s.get_score()
60
>>> s.score = 9999
>>>Traceback (most recent call last):
>>> ...
>>>ValueError: score must between 0 ~ 100!

参考资料:

  1. stackflow高赞讲解装饰器

  2. 菜鸟教程

  3. python装饰器的原理

Welcome to my other publishing channels