python之类与面向对象

1、面向对象的概念

段子手觉得面向对象对程序员来说真是个扯淡的东西,没有对象怎么面向对象编程?

但是没有对象我们也可以创造对象!

面向对象是一种程序设计方法,和面向过程相比,面向对象的核心在于对象,一切皆为对象,一个对象就是一个封装体,具有状态和操作。定义好一个对象并给它赋予属性和方法之后,我们便可以在任何地方调用这个对象。

看以下实例

#!/usr/bin/python
# -*- coding: UTF-8 -*-

class obb:   #  obb 是一个对象
    def mult(x,y): # obb对象有mult方法
        print( x * y ) 
 
obb.mult(2,4)  # 调用obb对象的mult方法
obb.mult(3,7)

上述例子中我们首先定义了obb对象,给一个对象添加属性和方法之后,在程序里我们可以随时调用这个对象,实现不同的需求。

2、python中类和对象的概念

面向对象最重要的概念就是类(Class)和实例(Instance),类是抽象的模板,比如学生、老师、人类等都可以是一个类,而实例是根据类创建出来的一个个具体的“对象”,每个对象都拥有相同的方法,但各自的数据可能不同。

举个例子,学生是一个类,我们把张三李四都看作一个学生,这就是对于学生这个类的两个不同的实例,每个实例都有自己的数据,在这个例子中就是选课、成绩、学号等等。

以下是一个简单的 Python 类的例子:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
class Employee:
   '所有员工的基类'
   empCount = 0
 
   def __init__(self, name, salary):
      self.name = name
      self.salary = salary
      Employee.empCount += 1
   
   def displayCount(self):
     print (self.empCount)
 
   def displayEmployee(self):
      print ( self.name, self.salary)

t=Employee('xiaoming',1000)
t.displayCount()
t.displayEmployee()

# 运行结果
1
xiaoming 1000
  • empCount 变量是一个类变量,它的值将在这个类的所有实例之间共享。你可以在内部类或外部类使用 Employee.empCount 访问。为什么要创建这个类变量而不在方法中创造,其实是变量作用域的原因,类变量可以被类中的所有方法访问
  • self 代表类的实例,self 在定义类的方法时是必须有的,虽然在调用时不必传入相应的参数。需要注意的是self代表类的实例,而非类。
  • t是一个实例,我们可以给其赋予name、salary等属性,也可以调用存在的方法。

3、python类的说明

(1)、关于_ init _

init _ _是一个特殊的方法,在创建实例的时候,这个方法就会被调用,一般用于传递参数。

注意到_ init _ 方法的第一个参数永远是self,表示创建的实例本身,因此,在 _init __方法内部,就可以把各种属性绑定到self,因为self就指向创建的实例本身。

有了_ init _ 方法,在创建实例的时候,就不能传入空的参数了,必须传入与_ init _ 方法匹配的参数,但self不需要传,Python解释器自己会把实例变量传进去:

>>> t=Employee('xiaoming',1000)
>>> t.name
'gengyanqing'
>>> t.salary
1000

需要注意的是,在类的方法中,第一个参数永远是self,调用时不用传递这个参数。除此之外类的方法和普通的函数没有太大的区别。

(2)、访问类的属性以及其内置属性

除了使用 t.name , 我们还可以通过以下函数来访问类的属性:

  • getattr(obj, name[, default]) : 访问对象的属性。
  • hasattr(obj,name) : 检查是否存在一个属性。
  • setattr(obj,name,value) : 设置一个属性。如果属性不存在,会创建一个新属性。
  • delattr(obj, name) : 删除属性。

举例:

>>> t.name # t的name属性
'gengyanqing'
>>> t.salary # t的salary属性
1000
>>> getattr(t,'name') # 获取t.name
'gengyanqing'
>>> hasattr(t,'name') # 判断是否存在t.name
True
>>> setattr(t,'name','xiaoming2') # t.name改为xiaoming2
>>> getattr(t,'name') 
'xiaoming2'
>>> delattr(t,'name') # 删除t的name属性
>>> hasattr(t,'name')
False

上述属性都是我们在定义类的时候赋予的,实际上python类含有很多内置属性,即只要你新建了类,系统就会自动创建这些属性。

1、 dict : 类的属性(包含一个字典,由类的数据属性组成)
2、_ doc _ :类的文档字符串
3、_ module _ : 类定义所在的模块(类的全名是' main_ .className',如果类 位于 一个导入模块mymod中,那么className. _ module 等于 mymod)

>>> t.__dict__
{'name': 'gengyanqing', 'salary': 1000}
>>> t.__doc__
'所有员工的基类'
>>> t.__module__
'__main__'

还有很多其他的内置属性,这里只列举了常见的。

怎么查看类的所有属性和方法,我们可以使用dir()函数:

>>> dir('t')
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

(3)、类的私有属性和私有方法

如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线_ ,在Python中,实例的变量名如果以 _开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问,所以,我们把Employee类改一改:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
class Employee: 
   def __init__(self, name, salary):
      self.name = name
      self.__salary = salary
      
# 调用结果
>>> t=Employee('xiaoming',10000)
>>> t.salary
Traceback (most recent call last):
  File "<pyshell#21>", line 1, in <module>
    t.salary
AttributeError: 'Employee' object has no attribute 'salary'
>>> t.__salary
Traceback (most recent call last):
  File "<pyshell#22>", line 1, in <module>
    t.__salary
AttributeError: 'Employee' object has no attribute '__salary'

可以看出我们无法通过t来访问_ _ salary属性,也无法修改这个属性。我们可以在类中写一个方法返回这个属性,然后在外部调用即可访问。

不仅是属性,我们也可以定义私有方法。两个下划线开头,声明该方法为私有方法,不能在类地外部调用。在类的内部调用

#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
class JustCounter:
    __secretCount = 0  # 私有变量
    publicCount = 0    # 公开变量
 
    def count(self):
        self.__secretCount += 1
        self.publicCount += 1
        print self.__secretCount
 
counter = JustCounter()
counter.count()
counter.count()
print counter.publicCount
print counter.__secretCount  # 报错,实例不能访问私有变量

Python不允许实例化的类访问私有数据,但我们可以使用 object. className _ attrName 访问属性。

#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
class Employee: 
   def __init__(self, name, salary):
      self.name = name
      self.__salary = salary
      
# 调用结果
>>> t=Employee('xiaoming',10000)
>>> t._Employee__salary
10000

那么定义私有属性的目的何在:原因是我们可以在内部对其修改做一定的限制。这个机制其实没啥卵用,我们还可以通过其他方式在外部访问这个属性,主要是要警告我们要自觉,不要干坏事。

(4)、类的继承和重写

面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制。继承完全可以理解成类之间的类型和子类型关系。
举例:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
class Parent:        # 定义父类
   def __init__(self):
      print ("调用父类构造函数")
 
   def parentMethod(self):
      print ('调用父类方法')
 
class Child(Parent): # 定义子类
   def __init__(self):
      print ("调用子类构造方法")
 
   def childMethod(self):
      print( '调用子类方法')

t=Child()
t.childMethod()
t.parentMethod()

执行结果:

调用子类构造方法
调用子类方法
调用父类方法

上述例子中可以看出。Child类继承了Parent类,也就具有了父类的方法。啥事也没干,就获得了parentMethod方法。
需要注意的是:在继承中基类的构造( init ()方法)不会被自动调用

当然,我们也可以继承多个父类:

class A:        # 定义类 A
.....

class B:         # 定义类 B
.....

class C(A, B):   # 继承类 A 和 B
.....

同样如果我们不喜欢父类的方法,我们也可以在子类中进行重写:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
class Parent:        # 定义父类
   def __init__(self):
      print ("调用父类构造函数")
 
   def parentMethod(self):
      print ('调用父类方法')

 
class Child(Parent): # 定义子类
   def __init__(self):
      print ("调用子类构造方法")
 
   def childMethod(self):
      print( '调用子类方法')

   def parentMethod(self):
      print ('这个还是子类方法')

t=Child()
t.childMethod()
t.parentMethod()

结果:

调用子类构造方法
调用子类方法
这个还是子类方法

重写的原因在于:
Python总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找。(先在本类中查找调用的方法,找不到才去基类中找)。

(5)、关于下划线说明

1、单下划线、双下划线、头尾双下划线说明:
foo _: 定义的是特殊方法,一般是系统定义名字 ,类似 _ init () 之类的。

2、_ foo: 以单下划线开头的表示的是 protected 类型的变量,即保护类型只能允许其本身与子类进行访问,不能用于 from module import *

3、_ _ foo: 双下划线的表示的是私有类型(private)的变量, 只能是允许这个类本身进行访问了。

3、总结

python面向对象可以说是python高级编程的基础了,不断的编程实践才能出真知。
关于多态,python类的构造析构,对象的深入理解等还有很多知识需要理解。本文如有错误,欢迎指正。

参考:
廖雪峰官方网站:https://www.liaoxuefeng.com
菜鸟教程:http://www.runoob.com
python核心编程第二版

1 + 7 =
快来做第一个评论的人吧~