[Python] 08 - Classes --> Objects

故事背景


1、阶级关系

1. Programs are composed of modules.
2. Modules contain statements.
3. Statements contain expressions.
4. Expressions create and process objects.html

 

 

2、教学大纲 

  • <Think Python>

 

  • 菜鸟教程

Goto: http://www.runoob.com/python3/python3-class.htmlpython

 

  

3、考点

Ref: [Advanced Python] 11 - Implement a Classc++

[面向对象]express

1、类的定义api

一个类,构造方法,self参数,“私有”属性&方法,数组

2、类的字典属性app

去查询:属性&方法 getattr()、setattr() 以及 hasattr()。函数

去模拟:字典的用法 __getattr__、__setattr__ 以及 __delattr__。post

3、类的实例this

实例的属性,类的属性

类的函数化:__call__ 

 

[类的继承]

1、单继承

调用父类的构造函数:<父类名>.__init__(self, <参数>)

2、多继承

把“父类名”改成“子类的父亲”:super(<子类名>, <类的实例>).__init__(self, *args, **kwargs)

3、类方法覆盖

多态的运用,甚至支持“鸭子类型'。

 

[类的”变量化“]

1、类的属性

也就是类的字典列表所得结果。

2、装饰器

@property, @xxx.setter,当作变量同样去使用。  

3、重载运算符

类内部属性示范。

 

 

 

 

面向对象


数据封装(Encapsulation )、继承(inheritance )和多态(polymorphism)是面向对象的三大特色。

1、类的定义

一个类

#!/usr/bin/python3
 
class MyClass:
    """一个简单的类实例"""
i= 12345 def f(self): return 'hello world'
# 实例化类 x = MyClass() # 访问类的属性和方法 print("MyClass 类的属性 i 为:", x.i) print("MyClass 类的方法 f 输出为:", x.f())

 

构造方法

赋值的的过程,就自动完成了类内的变量定义。

#!/usr/bin/python3
 
class Complex:
    def __init__(self, realpart, imagpart):
        self.r = realpart
        self.i = imagpart
x
= Complex(3.0, -4.5) print(x.r, x.i) # 输出结果:3.0 -4.5

 

self 参数

类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称

记录了 “类的地址”,“类的属性” 等。

class Test:
    def prt(self):
        print(self)        # address print(self.__class__)  # name
 
t = Test()
t.prt()

执行结果:

<__main__.Test instance at 0x100771878>
__main__.Test

注意:self 的名字并非规定死的,也可使用 this,可是最好仍是按照约定使用 self。

 

“私有” 属性&方法

私有属性

构造方法给私有属性赋值。

加上双下划线定义属性为私有属性。

#!/usr/bin/python3
 
#类定义
class people:
    #定义基本属性
    name = ''
    age  = 0
#定义私有属性,私有属性在类外部没法直接进行访问 __weight = 0
#定义构造方法 def __init__(self,n,a,w): self.name = n self.age = a self.__weight = w def speak(self): print("%s 说: 我 %d 岁。" %(self.name, self.age)) # 实例化类 p = people('runoob',10,30) p.speak()

 

私有方法

加上双下划线定义属性为私有方法。

#!/usr/bin/python3
 
class Site:
    def __init__(self, name, url):
        self.name = name       # public
        self.__url = url       #private
 
    def who(self):
        print('name  : ', self.name)
        print('url : ', self.__url)
 
    def __foo(self):          # 私有方法
        print('这是私有方法')
 
    def foo(self):            # 公共方法
        print('这是公共方法')
        self.__foo()
 
x = Site('菜鸟教程', 'www.runoob.com')
x.who()        # 正常输出
x.foo()        # 正常输出
x.__foo()      # 报错

 

“私有化” 原理

双下划线开头的实例变量是否是必定不能从外部访问呢?其实也不是。不能直接访问__name是由于Python解释器对外把__name变量改为了_Student__name,因此,仍然能够经过_Student__name来访问__name变量:

>>> bart._Student__name
'Bart Simpson'

可是强烈建议你不要这么干,由于不一样版本的Python解释器可能会把__name改为不一样的变量名。

总的来讲就是,Python自己没有任何机制阻止你干坏事,一切全靠自觉。

 

最后注意下面的这种错误写法:__name与内部解释后的_stduent__name可不是一个变量了。

>>> bart = Student('Bart Simpson', 59)
>>> bart.get_name()
'Bart Simpson'
>>> bart.__name = 'New Name' # 设置__name变量!
>>> bart.__name
'New Name'

 

 

2、类的字典属性

查询 属性&方法

仅仅把属性和方法列出来是不够的,配合getattr()setattr()以及hasattr(),咱们能够直接操做一个对象的状态:

>>> hasattr(obj, 'x') # 有属性'x'吗?
True
>>> obj.x
9
>>> hasattr(obj, 'y') # 有属性'y'吗?
False
>>> setattr(obj, 'y', 19) # 设置一个属性'y'
>>> hasattr(obj, 'y') # 有属性'y'吗?
True
>>> getattr(obj, 'y') # 获取属性'y'
19
>>> obj.y # 获取属性'y'
19

能够传入一个default参数,若是属性不存在,就返回默认值:

>>> getattr(obj, 'z', 404) # 获取属性'z',若是不存在,返回默认值404
404

 

模拟 字典效果

若是咱们在Class没有定义这3个方法,那么系统会用Python自带的内部函数;

若是咱们在类里面自定义了这3个函数,那么python会先调用咱们本身定义的这3个函数。

Ref: c到底干了什么?

这里要当心“无限循环”!须要经过__dict__操做。

class Cat:

    class_level = '贵族'

    def __init__(self,name,type,speed,age):
        self.name  = name
        self.type  = type
        self.speed = speed
        self.age   = age


    def run(self):
        print('%s岁的%s%s正在以%s的速度奔跑' % (self.age, self.type, self.name, self.speed))

    def __getattr__(self, item):
        print('你找的属性不存在')

    def __setattr__(self, key, value):
        print('你在设置属性')
        # self.key=value   #这种方法不行,会产生无限递归了,由于他自己self.key=value也会触发__setattr__
        self.__dict__[key] = value #咱们在给对象属性赋值的时候,内部原理就是操做对象的__dict__字典,因此咱们能够直接操做对象的字典实现属性赋值

    #删除属性的时候会触发
    def __delattr__(self, item):
        print('你在删除属性')
        # del self.item   #无限递归了,和上面的__setattr__原理同样
        self.__dict__.pop(item)

#实例化,会到__init__函数里去给实例数据属性赋值
xiaohua = Cat('小花','波斯猫','10m/s',10)

 

做用:动态调用 (unknown)

能够把一个类的全部属性和方法调用所有动态化处理了,不须要任何特殊手段。这种彻底动态调用的特性有什么实际做用呢?

做用就是,能够针对彻底动态的状况做调用:

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__    # 不返回地址,返回string; __repr__能够把对象转变为字符串的形式

Output:

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

>>> Chain().status /status

 

 

3、类的实例

实例属性 & 类属性

动态语言的特色,二者不是一个东西。

>>> class Student(object):
...     name = 'Student'
...
>>> s = Student() # 建立实例s
>>> print(s.name) # 打印name属性,由于实例并无name属性,因此会继续查找class的name属性
Student
>>> print(Student.name) # 打印类的name属性
Student
>>> s.name = 'Michael' # 给实例绑定name属性
>>> print(s.name) # 因为实例属性优先级比类属性高,所以,它会屏蔽掉类的name属性
Michael
>>> print(Student.name) # 可是类属性并未消失,用Student.name仍然能够访问
Student
>>> del s.name # 若是删除实例的name属性
>>> print(s.name) # 再次调用s.name,因为实例的name属性没有找到,类的name属性就显示出来了
Student

 

实例对象“函数化”

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

    def __call__(self):
        print('My name is %s.' % self.name)

>>> s = Student('Michael') >>> s() # self参数不要传入 My name is Michael.

那么,怎么判断一个变量是对象仍是函数呢?

>>> callable(Student())
True
>>> callable(max)
True
>>> callable([1, 2, 3])
False
>>> callable(None)
False
>>> callable('str')
False

 

 

 

类的继承


1、单继承

#!/usr/bin/python3
 
# 父类定义
class people:
    #定义基本属性
    name     = ''
    age      = 0
    #定义私有属性,私有属性在类外部没法直接进行访问
    __weight = 0
#定义构造方法 def __init__(self,n,a,w): self.name = n self.age = a self.__weight = w
def speak(self): print("%s 说: 我 %d 岁。" %(self.name,self.age))
#单继承示例 class student(people): grade = ''
def __init__(self, n, a, w, g): #调用父类的构函 people.__init__(self,n,a,w)  # <---- 这个方法可能不是很好 self.grade = g
#覆写父类的方法 def speak(self): print("%s 说: 我 %d 岁了,我在读 %d 年级"%(self.name,self.age,self.grade)) s = student('ken',10,60,3) s.speak()

 

 

2、多继承

(1) 倒三角:调用歧义

(1) 如果父类中有相同的方法名,而在子类使用时未指定,python 从左至右 搜索 即方法在子类中未找到时,从左到右查找父类中是否包含方法。

(2) 子类不重写 __init__,实例化子类时,会自动调用父类定义的 __init__。

#!/usr/bin/python3
 
#类定义
class people:
    #定义基本属性
    name = ''
    age = 0
    #定义私有属性,私有属性在类外部没法直接进行访问
    __weight = 0
    #定义构造方法
    def __init__(self,n,a,w):
        self.name = n
        self.age = a
        self.__weight = w
    def speak(self):
        print("%s 说: 我 %d 岁。" %(self.name,self.age))
 
#单继承示例 class student(people): grade = '' def __init__(self,n,a,w,g): #调用父类的构函 people.__init__(self,n,a,w) self.grade = g
#覆写父类的方法 def speak(self): print("%s 说: 我 %d 岁了,我在读 %d 年级"%(self.name,self.age,self.grade)) #另外一个类,多重继承以前的准备 class speaker(): topic = '' name = '' def __init__(self,n,t): self.name = n self.topic = t def speak(self): print("我叫 %s,我是一个演说家,我演讲的主题是 %s"%(self.name,self.topic))
#多重继承 class sample(speaker, student):    # <---- 多继承 a ='' def __init__(self,n,a,w,g,t): student.__init__(self, n, a, w, g) speaker.__init__(self, n, t) test = sample("Tim",25,80,4,"Python") test.speak() #方法名同,默认调用的是在括号中排前面的父类的方法

 

(2) 菱形:路径二义性

钻石继承时,c++中采用虚函数解决;python 怎么办?

参见高级部分:[Advanced Python] 11 - Implement a Class

class Father(object):
    def __init__(self, name, *args, **kwargs):
        self.name = name
        print("我是父类__init__")

class Son_1(Father): def __init__(self, name, age, *args, **kwargs): print("我是Son_1的__init__") super(Son_1, self).__init__(name, *args, **kwargs) self.age = age class Son_2(Father): def __init__(self, name, gender, *args, **kwargs): print("我是Son_2的__init__") self.gender = gender super(Son_2, self).__init__(name, *args, **kwargs)
class GrandSon(Son_1, Son_2): def __init__(self, name, age, gender): super(GrandSon, self).__init__(name, age, gender) def say_hello(self): print(self.name, self.age, self.gender) grand_son = GrandSon("老王", 24, "")

 

  

3、类方法覆盖

方法重写

实例方法 被覆盖

经过 super(<子类名>, <子类对象>).<父类方法> 调用“被覆盖的” 父方法。

#!/usr/bin/python3
 
class Parent:        # 定义父类
   def myMethod(self):
      print ('调用父类方法')
 
class Child(Parent): # 定义子类
   def myMethod(self):
      print ('调用子类方法')
 
c = Child()          # 子类实例
c.myMethod()         # 子类调用重写方法
super(Child, c).myMethod() #用子类对象调用父类已被覆盖的方法

输出:

调用子类方法
调用父类方法

 

类构造方法 被覆盖

这里提供了两种方式。

(1)
super(子类,self).__init__(参数1,参数2,....)

(2) 父类名称.
__init__(self, 参数1,参数2,...)

 

多态表达

"父类" 做为参数

animal做为父类,支持子类做为参数。

def run_twice(animal):  # 参数用的是”父类“,也能够直接带入"其子类"
    animal.run()
    animal.run()

 

"类父类" 做为参数

动态语言支持“鸭子类型”,具有“必要的”方法就能凑活的用了。

对于Python这样的动态语言来讲,则不必定须要传入Animal类型。咱们只须要保证传入的对象有一个run()方法就能够了:

class Timer(object):
    def run(self):
        print('Start...')

 

 

 

类的"变量化" 


1、类的属性

类的专有方法

__init__ : 构造函数,在生成对象时调用
__del__  : 析构函数,释放对象时使用
__repr__ : 打印,转换 __setitem__ : 按照索引赋值 __getitem__ : 按照索引获取值 __len__ : 得到长度 __cmp__ : 比较运算 __call__: 函数调用
__add__ : 加运算 __sub__ : 减运算 __mul__ : 乘运算 __div__ : 除运算 __mod__ : 求余运算 __pow__ : 乘方

 

 

2、装饰器

一些要点

    • 只有@property表示只读。
    • 同时有@property和@x.setter表示可读可写。
    • 同时有@property和@x.setter和@x.deleter表示可读可写可删除。

过去的策略

分别定义set, get函数操做。

装饰器策略

将函数当作属性来使用。

 

封装性

对外的api是 score 和 name。对内的变量实则 _score 和 _name。

class student(object):  #新式类
    
    def __init__(self,id):  
        self.__id=id  
        
    @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 and 100')   
        self._score=value  
               
    @property #只读
    def get_id(self):  
        return self.__id  
    
  
s
=student('123456') s.score=100 # print(s.score) # print(s.__dict__) print (s.get_id) # 只读 #s.get_id=456 #只能读,不可写: AttributeError: can't set attribute
================================================================
class A(object): # 新式类(继承自object类) def __init__(self): self.__name=None def getName(self): return self.__name def setName(self,value): self.__name=value def delName(self): del self.__name name=property(getName, setName, delName) a=A() print(a.name) # a.name='python' # print(a.name) # del a.name # 删除 #print a.name #a.name已经被删除 AttributeError: 'A' object has no attribute '_A__name'

 

再一个例子:展现deleter的使用。

class test1:  #经典类:没有继承object    
    def __init__(self):    
        self.__private='alex 1' #私有属性以2个下划线开头 
        
    #读私有属性    
    @property    
    def private(self):    
        return self.__private 
    
    #尝试去写私有属性(对于经典类而言,“写”是作不到的)  
    @private.setter    
    def private(self,value):    
        self.__private=value
        
    #尝试去删除私有属性(对于经典类而言,“删除”也是作不到的)  
    @private.deleter
def private(self): del self.__private ==================================================== class test2(object):# 新式类:继承了object def __init__(self): self.__private='alex 2' #私有属性以2个下划线开头 #读私有属性 @property def private(self): return self.__private #写私有属性 @private.setter def private(self,value): self.__private=value #删除私有属性 @private.deleter def private(self): del self.__private

t1
=test1() #print t1.__private # 外界不可直接访问私有属性 print (t1.private) # 读私有属性 print (t1.__dict__) t1.private='change 1' #对于经典类来讲,该语句其实是改变了实例t1的实例变量private print (t1.__dict__) print (t1.private) # 输出刚刚添加的实例变量private t1.private='change 2' print (t1.__dict__) del t1.private # 删除刚刚添加的实例变量private print (t1.__dict__) #print (t1.private) #读私有属性,由于已经删除,因此这里会报错
print ('-------------------------------------------------------') t2=test2() print (t2.__dict__) print (t2.private) # 继承了object,添加@private.setter后,才能够写 t2.private='change 2' # 修改私有属性 print (t2.__dict__) print (t2.private) del t2.private #删除私有变量 #print t2.private #私有变量已经被删除,执行“读”操做会报错:AttributeError: 'test2' object has no attribute '_test2__private' print (t2.__dict__)

 

 

3、运算符重载

类的加法

关键字:__add__

#!/usr/bin/python3
 
class Vector:
   def __init__(self, a, b):
      self.a = a
      self.b = b
 
   def __str__(self):
      return 'Vector (%d, %d)' % (self.a, self.b)
   
   def __add__(self, other):
      return Vector(self.a + other.a, self.b + other.b)
 
v1
= Vector(2,10) v2 = Vector(5,-2) print (v1 + v2)

执行结果:

Vector(7,8)

   

打印类(的字符串)

知识点:直接显示变量调用的不是__str__(),而是__repr__(),二者的区别是__str__()返回用户看到的字符串,而__repr__()返回程序开发者看到的字符串,也就是说,__repr__()是为调试服务的。

class people:
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def __str__(self):
        return '这我的的名字是%s,已经有%d岁了!'%(self.name,self.age)

a=people('孙悟空',999)
print(a)

 执行结果:

这我的的名字是孙悟空,已经有999岁了!

# 若是没有重载函数的话输出的就是一串看不懂的字符串:
# <__main__.people object at 0x00000272A730D278>

 

遍历类(内部的next)

以前是经过函数实现;这里则是“类的形式”,看上去比较好一些。

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 # 返回下一个值

# Fib()返回的是本身自己;迭代的则是__next__提供的结果。 >>> for n in Fib():   ... print(n) ... 1 1 2 3 5 ... 46368 75025

 

取元素

数组模式

本质上仍是经过从新计算得出结果。

class Fib(object):
    def __getitem__(self, n):
        a, b = 1, 1
        for x in range(n):
            a, b = b, a + b
        return a


>>> f = Fib()
>>> f[0] 1
>>> f[1] 1
>>> f[2]
2
>>> f[3]
3
>>> f[10]
89
>>> f[100]
573147844013817084101

 

切片模式

class Fib(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 >>> f = Fib() >>> f[0:5] [1, 1, 2, 3, 5] >>> f[:10] [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

与之对应的是__setitem__()方法,把对象视做list或dict来对集合赋值。最后,还有一个__delitem__()方法,用于删除某个元素。

 

End.

相关文章
相关标签/搜索