2020-04-08
0 目录
一、模块
- 举例子
- 变量类型
二、面向对象初级
- 类&实例
- 访问限制
- 继承多态
- 获取对象实例信息(属性方法)
- 实例属性vs类属性
三、面向对象高级
- __slots__
- @property
一、模块
- 举个例子
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
' a test module ' # 可以用moduleName.__doc__访问
__author__ = 'Michael Liao' # 可以用moduleName.__author__访问
import sys
def test():
args = sys.argv
if len(args)==1:
print('Hello, world!')
elif len(args)==2:
print('Hello, %s!' % args[1])
else:
print('Too many arguments!')
if __name__=='__main__': # 当在命令行执行此脚本时,会将特殊变__name__的置为__main__, 其他模块导入时,if判断会失效
test()
- 变量类型:
__xxx__
: 特殊变量,在类中的特殊变量是可以被直接访问的,_xxx
,__xxx
: 这样的函数或者变量称为“非公开的(private)”, 不应该被直接饮用,比如_abc
这种private变量或者函数不应该被引用,他们是有什么作用呢?
def _private_1(name):
return 'Hello, %s' % name
def _private_2(name):
return 'Hi, %s' % name
def greeting(name):
if len(name) > 3:
return _private_1(name)
else:
return _private_2(name)
当调用greeting的时候,用内部逻辑把private隐藏起来,这样调用greeting就不用关心其内部的private函数细节,作用是“代码封装和抽象”
二、面向对象初级
class Student(object):
pass
- 和普通方法相比,其内部的函数,第一个参数永远是'self', 表示创建的实例本身,其他使用都相同
- 类&实例(数据封装)
# 优点1. 从外部看Student, 实例化只需要提供name和age,关于如何打印,已经被类封装到内部,调用容易,但是不知道内部细节 # 优点2: 可以给Student类新增其他更多方法 class Student(object): def __ini__(self, name, score): # 每当有一个类被实例化的时候,都会调用一次__init__方法 self.name = name self.score = score def print_score(self): print("%s, %s" % (self.name, self.score)) bart = Student("alex", 50) bart.print_score() 既然这个实例内部本身就能访问到对应数据,就不用讲print_score写到外面 错误示例如下: class Student(object): def __ini__(self, name, score): self.name = name self.score = score bart = Student("alex", 50) def print_score(std): print('%s: %s' % (std.name, std.score)) print_score(bart)
-
访问限制
# 1. 为了避免外部代码随意修改实例的name和score,使用__命名的方式来限制实例对属性的直接访问, 从外部直接访问bart.__name会报错 # 2. 如果要访问或者修改怎么办?——通过方法来获取 # 3. 如果我有两个属性, 一个是_name, 另一个是__name, 这两个属性什么意思呢? # - _name这个实例变量在外部是可以访问的,但是警告你不要随意访问 # - __name这个实例变量在外部是不能访问的,但是python解释器会把双下划綫的变量修改为_Student__name, 所以在外面还是可以访问的 # 4. 那bart.name = "alex"可以直接修改啊,为什么要写一个方法来修改它?岂不是很浪费? # 答:在方法中,可以对参数做检查,避免无效的参数传入 class Student(object): def __init__(self, name, score): self.__name = name self.__score = score def print_score(self): print('%s: %s' % (self.__name, self.__score)) def get_name(self): return self.__name def set_name(self, name): if len(name) > 100 or len(name) < 0: raise ValueError('bad name len') else: self.__name = name
- 继承和多态
# 1. 继承:子类会继承父类的所有方法和功能, 子类可以继续扩展(覆盖)父类方法 # 2. 多态: 有关于如何理解多态,参考这里: https://www.liaoxuefeng.com/wiki/1016959663602400/1017497232674368 # 3. 一个class就是一种数据类型,比如list,dict, str等 class Animal(object): def run(self): print("animal can run!") class Dog(Animal): def run(self): print("Dog is Running") class Cat(Animal): print("Cat is Running") dog = Dog() dog.run() # 多态 a = list() b = Animal() c = Dog() isinstance(a, list) isinstance(b, Animal) isinstance(c, Dog) isinstance(c, Animal) # 结果都为True # 多态到底哪里好? def run_twice(animal): animal.run() animal.run() >>>run_twice(Animal()) "animal can run!" "animal can run!" >>>run_twice(Dog()) "Dog is Running" "Dog is Running" >>>run_twice(Cat()) "Cat is Running" "Cat is Running" # 总结多态好处 # 1. 当你一个函数需要传入Dog, Cat, Tortoise...时,只需要接受Animal类型就可以了,因为他们都是Animal # 2. 所以: 函数(run_twice)传入的任意类型,只要是animal的类或者子类,就会自动调用时机类型的run()方法,这就是多态的意思 # 3. 对扩展开放: 允许新增Animal子类: # 4. 对修改封闭: 不需要修改依赖Animal类型的run_twice()等函数
- 获取对象(实例)信息
# 1. 使用type() # 获取对象(实例)的类型信息 # 2. 使用isinstance() # 判断实例是否为某个类型 # 3. 使用dir() # 返回对象的所有属性和方法 # 4. hasattr(obj, 'x'), setattr(obj, 'y', 19), getattr(obj, 'y') >>>type('stringexample') >>>type(Dog()) >>>type(123) >>>def fn(): >>> pass >>>type(fn) == types.FunctionType >>>type(abs) == types.BuiltinFunctionType >>>type(lambda x: x*2) == types.LambdaType >>>type((x for x in range(10)))==types.GeneratorType # isinstance, 假设继承关系为object -> Animal -> Dog -> Husky >>> isinstance(h, Animal) >>> isinstance('a', str) >>> isinstance(b'a', bytes) >>> isinstance((1, 2, 3), (list, tuple)) // 是否是list或者tuple >>> dir('abc') ['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill'] >>> len('abc') == 'abc'.__len__() // len()方法其实就是调用对象的__len__()方法,两个等价 # 对象的属性和方法测试 >>> class MyObject(object): ... def __init__(self): ... self.x = 9 ... def power(self): ... return self.x * self.x ... >>> obj = MyObject() >>> hasattr(obj, 'x') # 是否有属性'x' >>> setattr(obj, 'y', 19) # 设置属性'y', 值为9 >>> getattr(obj, 'y', 100) # 获取obj对象的'y'属性值,如果没有则默认为100 >>> obj.y 19
- 实例属性 vs 类属性
# 1. python是动态语言,根据类创建的实例,可以任意绑定属性, 给实例绑定属性的方法是通过实例变量,或者通过self属性 # 2. 实例属性会覆盖类属性,不要对实例属性和类属性使用相同的名字 class Student(object): def __init__(self, name): self.name = name s = Student('Bob') s.score = 90 # 如何给Student类绑定属性? class Student(object): name = "Student" # 类属性归类所有,但是其所有实例都可访问 >>> s = Student() >>> print(s.name) >>> print(Student.name) # 如果不是类属性,这个会报错 Student
三、面向对象高级
__slots__
# 1. 通过MethodType给实例绑定方法 # 2. 通过动态方法,给class绑定方法 # 3. 通过在类中定义__slots__来限制实例能够添加的属性 # 4. __slots__仅对当前类生效,对其子类不生效, 子类如果也有__slots__, 那么其子类的限制就是子类+父类的总和 class Student(object): pass s = Student() s.name = "Michael" def set_age(self, age): self.age = age from types import MethodType s.set_age = MethodType(set_age, s) # 给实例绑定一个方法 s.set_age(25) def set_score(self, score): self.score = score >>> Student.set_score = set_score # 给class绑定一个方法 >>> s.set_score(100) >>> s.score >>> class Student(object): >>> __slots__ = ("name", "age") # 用tuple定义允许绑定的属性名称 >>> s = Student() >>> s.name = "Michael" # 可以绑定 >>> s.score = 99 # 因为啥偶先,所以会报AttributeError Traceback (most recent call last): File "
", line 1, in AttributeError: 'Student' object has no attribute 'score' - @property
# 在类中直接设置属性,可以将属性暴露出去,但是没办法检查参数,导致该属性可以随意被更改
# 为了限制属性,我们用set_score()的方式来设置成绩,在通过get_score()方法来获取
>>> class Student(object):
>>> def get_score(self):
>>> return self._score
>>> def set_score(self, value):
>>> if not instance(value, int):
>>> raise ValueError('score must be an int')
>>> if value < 0 or value > 100:
>>> raise ValueError('score must in 0-100')
>>> self._score = score
# 但是上面这种又有点复杂, 我要既能检查参数,又可以用“类似属性”这种简单的方式来访问类的变量
# 类中,@property这个内置的装饰器就是讲方法变成属性来调用的
>>> class Student(object):
>>> @property #将score这个getter方法变成属性,只需要加上@property这个装饰器即可
>>> def score(self):
>>> return self._score
>>>
>>> @score.setter # @property又提供创建另一个装饰器@score.setter, 它负责把一个setter方法变成属性赋值,于是我们就有一个可控的属性操作
>>> def score(self,value):
>>> if not instance(value, int):
>>> raise ValueError('score must be an int')
>>> if value < 0 or value > 100:
>>> raise ValueError('score must in 0-100')
>>> self._score = value
>>> s = Student()
>>> s.score = 60
>>> s.score
60
>>> s.score = 9999
这里会触发ValueError异常
# 注意: 如果只给一个getter方法设置了@property,不设置对应的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 2015 - self._birth
- 多重继承
参考这里: - 定制类(python class中的特殊函数)
# __str__ # 定义好__str__()方法,用于返回一个好看的字符串 class Student(object): def __init__(self,name): self.name = name def __str__(self): return "student obj (name: %s)" % self.name >>> print(Student('Michael') student obj (name: Michael) # 如果没有这个__str__就会返回一个这个 `<__main__.Student object at 0x109afb190>` # __repr__ # 上面的还需要print方法才能打印出来 class Student(object): def __init__(self, name): self.name = name def __str__(self): return 'Student obj (name=%s)' % self.name __repr__ = __str__ # __iter__ # 类的创建本身就是一个type的创建, 就比如列表也是一种type # 那么如果你想让你的类能够像列表一样被循环,for ... in 这种,就需要实现一个__iter__()方法
- 使用枚举类
- 使用元类