【深度学习】DAY 8 - Python入门(八)面向对象编程

深度学习DAY 8 - Python入门(八)

  • Chapter 1 Python入门
    • 1.7 面向对象编程
      • 1.7.1 类
        • (1)定义和使用类
          • 1)定义类
          • 2)使用类
        • (2)类的属性
          • 1)实例属性
          • 2)类属性
          • 3)修改属性的值
        • (3)类的方法
          • 1)构造函数 __ init __()
          • 2)限制访问
          • 3)类方法和静态方法
      • 1.7.2 数据封装
      • 1.7.3 继承
        • (1)直接继承
        • (2)给子类定义属性和方法
        • (3)重写父类的方法
        • (4)多重继承
      • 1.7.4 多态
      • 1.7.5 获取对象信息
        • (1)type()
        • (2)isinstance()
        • (3)dir()
        • (4)getattr()、setattr()、hasattr()

Chapter 1 Python入门

1.7 面向对象编程

面向对象编程(Object Oriented Programming),以对象作为程序的基本单元,其中对象包含了数据和操作数据的函数。
面向过程设计则将程序视作一系列命令集合,即通过函数来顺序运行,利用函数切分来降低复杂度。
面向对象设计则将程序视作一组对象的集合。对象可以接受、发送和处理信息。程序执行就是指消息在各对象的传递。
数据封装、继承和多态是面向对象的三大特点,后面会详细介绍。

1.7.1 类

Python中的所有数据类型都可以视为对象,而 类(class) 就是自定义的对象数据类型。根据类来创建对象,就是 实例化(Instance)

(1)定义和使用类

1)定义类
  • 结构
class<类名>(父类名):
	def __init__(self, 参数,): # 构造函数
		...
	def 方法名1(self, 参数,): # 方法1
		...
	def 方法名2(self, 参数,): # 方法2
		...

其中类名推荐首字母大写。

class Calculator: 
    name='Good Calculator'  #class的属性
    price=18
    def add(self,x,y):
        print(self.name)
        result = x + y
        print(result)
    def minus(self,x,y):
        result=x-y
        print(result)
    def times(self,x,y):
        print(x*y)
    def divide(self,x,y):
        print(x/y)

类定义函数时,与普通函数定义不同的是,参数第一个必须是self

2)使用类

实例化方式:实例名=类名()

>>> cal=Calculator()  #运行class的时候要加"()"
>>> cal.name
'Good Calculator'
>>> cal.price
18
>>> cal.add(10,20)
Good Calculator
30
>>> cal.minus(10,20)
-10
>>> cal.times(10,20)
200
>>> cal.divide(10,20)
0.5

(2)类的属性

数据含有自身的属性。Python中类的属性分为两类:实例属性和类属性。

1)实例属性

定义格式:self.属性名实例名.属性名

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

属性名是name

调用格式:实例名.属性名

>>> s = Student('Bob')
>>> s.name
'Bob'
2)类属性

定义格式:属性名= (class中直接赋值)

class Student(object):
    name = 'Bob'

调用格式:类名.类变量名

>>> print(Student.name)
Bob
3)修改属性的值
  • 给属性设定默认值。
class Calculator:
    name='good calculator'
    price=18
    def __init__(self,name,price,height=10,width=14,weight=16): #后面三个属性设置默认值,查看运行
        self.name=name
        self.price=price
        self.h=height
        self.wi=width
        self.we=weight
  • 可通过实例访问,即实例名.方法名赋值修改。
>>> c=Calculator('bad calculator',18)
>>> c.h
10
>>> c.wi
14
>>> c.we
16
>>> c.we=17
>>> c.we
17
  • 可以通过方法来修改,将值传递给方法,内部进行更新。【推荐!】
class Car:

	def __init__(self,make,model,year):
		self.make = make
		self.model = model
		self.year = year
		self.odometer = 0
		
	def get_name(self):
		name = str(self.year) + ' ' + self.make + ' ' + self.model
		return name.title()

	def read_odometer(self):
		print("This car has " + str(self.odometer) + " miles on it.")
	
	def update_odometer(self,mileage):
		self.update_odometer = mileage

	def update_odometer(self,mileage):
		self.odometer = mileage

进行实例化

my_car = Car('audi','a4',2016)
print(my_car.get_name())
my_car.read_odometer()

输出

2016 Audi A4
This car has 0 miles on it.

更新里程表

my_car.odometer = 23
my_car.read_odometer()

输出

This car has 23 miles on it.

实例属性和类属性不要使用相同的名字,否则实例属性会屏蔽类属性

(3)类的方法

类中的函数称为方法(method),与普通的函数不同的是调用的方式。

  • method的第一个参数必须是self
  • method调用需要实例化,以实例名.方法名的形式调用,来访问实例变量的属性。
1)构造函数 __ init __()

__init__() 方法用于实例化时相关数据的初始化(initial),也被称为构造函数(constructor),在生成类的实例时被调用一次。一般在创建实例的时候,将一些必须绑定的属性强制捆绑,可以使用构造函数。
注意前后分别有两个下划线,为特殊变量。

  • 定义类
class Calculator:
    name='good calculator'
    price=18
    def __init__(self,name,price,height,width,weight):
    	self.name=name
        self.price=price
        self.h=height
        self.wi=width
        self.we=weight 
  • 使用类和实例化
>>> c=Calculator('bad calculator',18,17,16,15)

实例化需要给定__init__() 中包含的所有参数的值

>>> c.name
'bad calculator'
>>> c.price
18
>>> c.h
17
>>> c.wi
16
>>> c.we
15
2)限制访问

Python私有变量(private)以两个下划线 __开头,只有内部可以访问。但是不能以两个下划线__结尾,否则是特殊变量。

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))

此时,外部无法访问实例变量.__name实例变量.__score

如果外部代码想要获取内部数据,可以在类中增加获取数据和修改数据的方法。

class Student(object):
    ...

    def get_name(self):
        return self.__name

    def get_score(self):
        return self.__score

    def set_score(self, score):
        self.__score = score    

比直接通过实例名.属性名进行数据修改更好的原因是,方法中可以对参数进行检查,避免传入无效的参数。

class Student(object):
    ...

    def set_score(self, score):
        if 0 <= score <= 100:
            self.__score = score
        else:
            raise ValueError('bad score')
3)类方法和静态方法

类的方法分为:实例方法、类方法、静态方法。
前文提到的都是实例方法,调用参数为实例。
类方法的调用参数是类。静态方法没有调用参数。两者调用前可以不实例化类。

  • 类方法
    使用装饰器@classmethod 进行修饰。其中类方法定义需要带默认参数。
    【装饰器部分后文详述】
  • 静态方法
    使用装饰器@staticmethod进行修饰

定义

class Method:
	@staticmethod
	def static_method():
		print('调用静态方法')
	@classmethod
	def class_method(c):
		print('调用类方法')

调用

Method.static_method()
调用静态方法
>>> Method.class_method()
调用类方法

1.7.2 数据封装

面向对象编程的其中一个重要特点就是数据封装。
封装,就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。

优点:隐藏细节

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))

实例化

>>> bob = Student()

捆绑数据属性

>>> bob.name
'Bob Smith'
>>> bart.score
80
  • 可以通过函数访问数据
 def print_score(std):
      print('%s: %s' % (std.name, std.score))
print_score(bob)
Bob Smith: 80
  • Student的实例本身含有以上数据,可以直接在Student类的内部定义访问数据的函数,而不需要在外部定义新函数进行访问,这就是数据封装。封装数据的函数和类本身是关联的,也就是前文提到的类的方法。

1.7.3 继承

一个类继承另一个类时,将获得另一个类所有属性和方法。
原有的类称为父类、基类或超类,新类称为子类或派生类
另外子类可以定义自己的属性和方法,也可以修改父类的方法,但是不能继承父类的私有属性和私有方法。
所有类都可以追溯到根类object。

优点:代码重用

使用方式 class 子类名(父类名)

class Animal(object):
    def run(self):
        print('Animal is running...')

(1)直接继承

class Dog(Animal):
    pass #表示空语句

class Cat(Animal):
    pass

此时方法run()得到继承

dog = Dog()
dog.run()

cat = Cat()
cat.run()
Animal is running...
Animal is running...

(2)给子类定义属性和方法

class Dog(Animal):
	def stop(self):
		print('Dog stops running.')
>>> dog = Dog()
>>> dog.run()
Animal is running...
>>> dog.stop()
Dog stops running.

(3)重写父类的方法

父类的方法不符合子类的行为, 都可对其进行重写。可在子类中定义与要重写的父类方法同名的方法。 这样, Python将不会考虑这个父类方法, 而只关注你在子类中定义的相应方法,也称作方法重载。

class Dog(Animal):

    def run(self):
        print('Dog is running...')

class Cat(Animal):

    def run(self):
        print('Cat is running...')
Dog is running...
Cat is running...

建议将类细节较多时,将大型类拆分成协同工作的小类

(4)多重继承

一个类可以继承多个父类,叫多重继承。
继承方式以“,” 隔开多个父类。
格式:class 子类名(父类1,父类2,...,父类n)

1.7.4 多态

多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。
简单的说,就是允许将父类的引用指向子类的对象。保证子类的正确调用。

优点:接口重用
对扩展开放(Open for extension):允许子类重写方法函数
对修改封闭(Closed for modification):不重写,直接继承父类方法函数

1.7.5 获取对象信息

(1)type()

判断对象类型,可使用type()函数。

  • 基本类型判断
>>> type(123)
<class 'int'>
>>> type('str')
<class 'str'>
>>> type(None)
<type(None) 'NoneType'>
  • 函数或类判断
>>> type(abs)
<class 'builtin_function_or_method'>
>>> type(a)
<class '__main__.Animal'>
  • type()函数返回对应的Class类型。可用于比较两个变量的基本类型是否相同。
>>> type(123)==type(456)
True
>>> type('abc')==str
True
>>> type('abc')==type(123)
False
  • 判断对象是否是函数时,可使用types模块中定义的常量。【模块部分后面详述】
>>> import types
>>> def fn():
...     pass
...
>>> type(fn)==types.FunctionType
True
>>> type(abs)==types.BuiltinFunctionType
True
>>> type(lambda x: x)==types.LambdaType
True
>>> type((x for x in range(10)))==types.GeneratorType
True

(2)isinstance()

由于类存在继承关系,使用type()会不方便,因此判断类的类型,可以使用isinstance()函数。【推荐!】

  • 类判断

以上面的例子,继承关系为

object -> Animal -> Dog -> Husky

接上文,创建对象

>>> a = Animal()
>>> d = Dog()
>>> h = Husky()

isinstance()判断

>>> isinstance(h, Husky)
True
>>> isinstance(h, Dog)
True
>>> isinstance(d, Husky)
False

可见,h属于Husky类本书,也属于父类Dog
因此,isinstance()判断一个对象是否为该类型本身,或者位于该类型的父继承链上。

  • 基本类型判断
>>> isinstance('a', str)
True
>>> isinstance(123, int)
True
>>> isinstance(b'a', bytes)
True
  • 多种类型判断
>>> isinstance([1, 2, 3], (list, tuple))
True

(3)dir()

dir()函数可获得一个对象的所有属性和方法。

>>> dir('ABC')
['__add__', '__class__',..., '__subclasshook__', 'capitalize', 'casefold',..., 'zfill']

__xxx__ 为特殊变量。如函数len()可获取对象的长度,其内部自动调用 __len__()方法

>>> len('ABC')
3
>>> 'ABC'.__len__()
3

(4)getattr()、setattr()、hasattr()

getattr()setattr()以及hasattr()可以直接操作对象的状态。
getattr()用于获取属性,getattr(实例名, '属性名', <默认值>),可传入default参数。
setattr()用于设置属性,setattr(实例名, '属性名', 属性值)
hasattr()用于判断对象是否包含某种属性,hasattr(实例名, '属性名'),输出True或False。

定义对象

>>> class MyObject(object):
		def __init__(self):
       		self.x = 9
   		def power(self):
      		return self.x * self.x

>>> obj = MyObject()

测试属性

>>> 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

getattr()可传入default参数。

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

getattr()可获得对象同时赋值到变量

>>> fn = getattr(obj, 'power') # 获取属性'power'并赋值到变量fn
>>> fn() # 调用fn()与调用obj.power()是一样的
81

但是只有不知道对象信息时,才获取信息。可以直接实例化就不要用getattr()函数。

hasattr()判断函数对于代码执行有重要作用。

====================================================================
Python学习的内容参考
《Python编程:从入门到实践》-[美] Eric Matthes
《21天学通PYTHON》
莫烦Python
廖雪峰的Python教程

你可能感兴趣的:(Python,python)