python 类相关概念理解

目录
  • 什么是类,对象,实例,类## 标题变量(类属性),实例变量(实例属性)
  • 类变量和类属性的分类
  • 类调用实例方法
  • 类的封装(enclosure),继承和多态
  • 迭代,迭代器(iterator),可迭代对象(iterable object),生成器(generator)
  • 总结

什么是类,对象,实例,类## 标题变量(类属性),实例变量(实例属性)

面向对象编程(Object-oriented Programming,简称 OOP),是一种封装代码的方法。比如说,将乱七八糟的数据扔进列表中,这就是一种简单的封装,是数据层面的封装;把常用的代码块打包成一个函数,这也是一种封装,是语句层面的封装。面向对象编程,也是一种封装的思想,把描述特征的数据和代码块(函数)封装到一起。

面向对象中,常用术语包括类,对象,属性,方法:

:可以理解是一个模板,通过它可以创建出无数个具体实例(又称对象)。
对象(实例):类并不能直接使用,通过类创建出的实例才能使用。
属性:类中的所有变量称为属性。
方法:类中的所有函数通常称为方法。不过,和函数所有不同的是,类方法至少要包含一个 self 参数。类方法无法单独使用,只能和类的对象一起使用。

类变量和类属性的分类

无论是类属性还是类方法,都无法像普通变量或者函数那样,在类的外部直接使用它们。我们可以将类看做一个独立的空间,则类属性其实就是在类体中定义的变量,类方法是在类体中定义的函数。

在类体中,根据变量定义的位置不同,以及定义的方式不同,类属性又可细分为以下 3 种类型:

类体中、所有函数之外:此范围定义的变量,称为类属性或类变量;
类体中,所有函数内部:以“self.变量名”的方式定义的变量,称为实例属性或实例变量;
类体中,所有函数内部:以“变量名=变量值”的方式定义的变量,称为局部变量。

和类属性一样,类方法也可以进行更细致的划分,具体可分为类方法、实例方法和静态方法。在实际编程中,几乎不会用到类方法和静态方法,因为我们完全可以使用函数代替它们实现想要的功能,但在一些特殊的场景中(例如工厂模式中),使用类方法和静态方法也是很不错的选择。

和类属性的分类不同:
类方法:采用 @classmethod 修饰的方法
静态方法:采用 @staticmethod> 修饰的方法
实例方法:不用任何修改的方法

构造方法:在创建类时,我们可以手动添加一个 init() 方法,该方法是一个特殊的类实例方法,称为构造方法(或构造函数)。构造方法用于创建对象时使用,每当创建一个类的实例对象时,Python 解释器都会自动调用它。

类调用实例方法

实例方法的调用方式其实有 2 种,既可以采用类对象调用,也可以直接通过类名调用。

class CLanguage :
    # 下面定义了2个类变量
    name = "python语言"
    add = "http://c.biancheng.net.python"
    # __init__是构造方法,也属于实例方法
    def __init__(self,name,add):
        #下面定义 2 个实例变量
        self.name = name
        self.add = add
        print(name,"网址为:",add)
    # 下面定义了一个say实例方法
    def say(self, content):
        print(content)
    #下面定义了一个count实例方法
    def count(self,money):
    #下面定义了一个局部变量sale
        sale = 0.8*money
        print("优惠后的价格为:",sale)
    #下面定义了一个类方法
    @classmethod
    def info(cls):
        print("正在调用类方法",cls)
    #下面定义了一个静态方法
    @staticmethod
    def info(name,add):
        print(name,add)
# 将该CLanguage对象赋给clanguage变量
# 通过类名直接调用实例方法
# CLanguage.say("通过类名直接调用实例方法")会报错,必须手动将 clang 这个类对象传给self 参数
clang = CLanguage("C语言中文网1","http://c.biancheng.net")#传入的参数要和init的一样
CLanguage.say(clang, "通过类名直接调用实例方法")
# 通过类对象直接调用实例方法
clang2 = CLanguage("C语言中文网2","http://c.biancheng.net")
clang2.say("通过类对象直接调用实例方法")
#输出
C语言中文网1 网址为: http://c.biancheng.net
通过类名直接调用实例方法
C语言中文网2 网址为: http://c.biancheng.net
通过类对象直接调用实例方法

类的封装(enclosure),继承和多态

1.封装

简单的理解封装(Encapsulation),即在设计类时,刻意地将一些属性和方法隐藏在类的内部,这样在使用此类时,将无法直接以“类对象.属性名”(或者“类对象.方法名(参数)”)的形式调用这些属性(或方法),而只能用未隐藏的类方法间接操作这些隐藏的属性和方法。

Python 类如何进行封装?

和其它面向对象的编程语言(如 C++、Java)不同,Python 类中的变量和函数,不是公有的(类似 public 属性),就是私有的(类似 private),这 2 种属性的区别如下:

public:公有属性的类变量和类函数,在类的外部、类内部以及子类(后续讲继承特性时会做详细介绍)中,都可以正常访问;

private:私有属性的类变量和类函数,只能在本类内部使用,类的外部以及子类都无法使用。

Python 并没有提供 public、private 这些修饰符。为了实现类的封装,Python 采取了下面的方法:
默认情况下,Python 类中的变量和方法都是公有(public)的,它们的名称前都没有下划线(_);
如果类中的变量和函数,其名称以双下划线“”开头,但是没有以双下划线“”结尾,则该变量(函数)为私有变量(私有函数),其属性等同于
private。

封装的具体细节参考

2.继承和多态

在OOP(Object Oriented Programming)程序设计中,当我们定义一个class的时候,可以从某个现有的class 继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。

继承有什么好处?最大的好处是子类获得了父类的全部属性及功能。

使用 class subclass_name(baseclass_name) 来表示继承

class Person(object):
    def __init__(self,name,sex):
        self.name = name
        self.sex = sex
    def print_title(self):
        if self.sex == "male":
            print("man")
        elif self.sex == "female":
            print("woman")
class Child(Person):                            # Child 继承 Person
    pass
May = Child("May","female")
Peter = Person("Peter","male")
print(May.name,May.sex,Peter.name,Peter.sex)    # 子类继承父类方法及属性
May.print_title()
Peter.print_title()
#输出
May female Peter male
woman
man

isinstance() 及 issubclass()

Python 与其他语言不同点在于,当我们定义一个 class 的时候,我们实际上就定义了一种数据类型。我们定义的数据类型和Python自带的数据类型,比如str、list、dict没什么两样。

Python 有两个判断继承的函数:isinstance() 用于检查实例类型;issubclass() 用于检查类继承。参见下方示例:

class Person(object):
    pass
class Child(Person):                 # Child 继承 Person
    pass
May = Child()
Peter = Person()
print(isinstance(May,Child))         # True
print(isinstance(May,Person))        # True
print(isinstance(Peter,Child))       # False
print(isinstance(Peter,Person))      # True
print(issubclass(Child,Person))      # True

在说明多态是什么之前,我们在 Child 类中重写 print_title() 方法:若为male,print boy;若为female,print girl

class Person(object):
    def __init__(self,name,sex):
        self.name = name
        self.sex = sex
    def print_title(self):
        if self.sex == "male":
            print("man")
        elif self.sex == "female":
            print("woman")
class Child(Person):                # Child 继承 Person
    def print_title(self):
        if self.sex == "male":
            print("boy")
        elif self.sex == "female":
            print("girl")
May = Child("May","female")
Peter = Person("Peter","male")
print(May.name,May.sex,Peter.name,Peter.sex)
May.print_title()
Peter.print_title()

当子类和父类都存在相同的 print_title()方法时,子类的 print_title() 覆盖了父类的 print_title(),在代码运行时,会调用子类的 print_title()

这样,我们就获得了继承的另一个好处:多态。

多态的好处就是,当我们需要传入更多的子类,例如新增 Teenagers、Grownups 等时,我们只需要继承 Person 类型就可以了,而print_title()方法既可以直不重写(即使用Person的),也可以重写一个特有的。这就是多态的意思。调用方只管调用,不管细节,而当我们新增一种Person的子类时,只要确保新方法编写正确,而不用管原来的代码。这就是著名的**“开闭”原则**:

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

子类重写构造函数

子类可以没有构造函数,表示同父类构造一致;子类也可重写构造函数;现在,我们需要在子类 Child 中新增两个属性变量:mother 和 father,我们可以构造如下(建议子类调用父类的构造方法,参见后续代码):

class Person(object):
    def __init__(self,name,sex):
        self.name = name
        self.sex = sex
class Child(Person):                          # Child 继承 Person
    def __init__(self,name,sex,mother,father):
        Person.__init__(self,name,sex)        # 子类对父类的构造方法的调用
        self.mother = mother
        self.father = father
May = Child("May","female","April","June")
print(May.name,May.sex,May.mother,May.father)

多重继承

多重继承的概念应该比较好理解,比如现在需要新建一个类 baby 继承 Child , 可继承父类及父类上层类的属性及方法,优先使用层类近的方法,代码参考如下:

class Person(object):
    def __init__(self,name,sex):
        self.name = name
        self.sex = sex
    def print_title(self):
        if self.sex == "male":
            print("man")
        elif self.sex == "female":
            print("woman")
class Child(Person):
    pass
class Baby(Child):
    pass
May = Baby("May","female")        # 继承上上层父类的属性
print(May.name,May.sex)         # May female
May.print_title()                 #woman 可使用上上层父类的方法

class Child(Person):
    def print_title(self):
        if self.sex == "male":
            print("boy")
        elif self.sex == "female":
            print("girl")
class Baby(Child):
    pass
May = Baby("May","female")
May.print_title()                # girl 优先使用上层类的方法

迭代,迭代器(iterator),可迭代对象(iterable object),生成器(generator)

迭代:是访问集合元素的一种方式。如果给定一个list或tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历我们称为迭代(Iteration)。

迭代对象: 如果类中定义了__iter__方法,且返回了一个迭代器对象, 那么称这个创建的对象为可迭代对象

字符串,列表,元祖,字典,集合等等,都是可迭代对象。
(没有__next__)
可迭代对象,则可用for循环

# 字符串,列表,元祖,字典,集合等等,都是可迭代对象
for i in [1, 2, 3]:
    print(i)
obj = {"a": 123, "b": 456}
for k in obj:
    print(k)

创建可迭代对象

class foo(object):
	def __iter__(self):
		return 迭代器对象(生成器对象)
obj = foo() #obj是可迭代对象
#创建迭代器
class IT(object):
	def __init__(self):
		self.counter = 0
	def __iter__(self):
		return self
	def __next__(self):
		self.counter += 1
		if self.counter == 3:
			raise StopIteration()
#创建可迭代器对象
class foo(object):
	def __iter__(self):
		return IT()
obj = foo() #obj是可迭代对象
for item in obj:   #循环可迭代对象时,内部先执行obj.__iter__()并获取迭代器对象,然后再不断的执行迭代器对象的__next__方法
	print(item)

迭代器:迭代器是一个可以记住遍历的位置的对象

类中定义了__iter__和__next__两个方法
__iter__返回对象本身,即self
__next__返回下一个数据,如果没有,就返回一个stopiteration 的异常。
迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
迭代器有两个基本的方法(内置函数):iter() 和 next() ;内置函数 iter() 将可迭代对象转化为迭代器. 通过 next() 方法逐一读取下一个元素

创建一个迭代器

创建迭代器共有3种方法如下:

通过python内置函数iter()将可迭代对象转换为迭代器
自己创建一个迭代器, 满足(1)类中定义了__iter__和__next__两个方法(2)__next__返回下一个数据,如果没有,就返回一个stopiteration> 的异常 2个条件
通过生成器(generator)创建

# 方法1: iter()将可迭代对象转换为迭代器
list=[1, 2, 3, 4]
it = iter(list)    # 创建迭代器对象
#使用next()遍历数据
print (next(it))  #1
print (next(it))  #2
#使用for循环遍历数据,for循环由于简洁,更常用
#for循环会执行迭代器的iter并获得返回的对象,一直反复的去执行next(对象)
for x in it:
    print (x, end=" ")   # 3 4
print (next(it))   #StopIteration
# 方法2: 创建一个迭代器
class MyNumbers:
  def __iter__(self):
    self.a = 1
    return self
  def __next__(self):
    if self.a <= 20:
      x = self.a
      self.a += 1
      return x
    else:
      raise StopIteration
#通过iter()和next()访问
myclass = MyNumbers()
myiter = iter(myclass)
print(next(myiter))
print(next(myiter))
print(next(myiter))
for x in myiter:
  print(x)
print(next(myiter))

#输出
StopIteration
1
2
3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Process finished with exit code 1

生成器:使用了 yield 的 函数 被称为生成器(generator)

具体细节参考https://www.jb51.net/article/63929.htm

相比迭代器,生成器最明显的优势就是节省内存空间,即它不会一次性生成所有的数据,而是什么时候需要,什么时候生成。

创建一个生成器

定义一个以 yield 关键字标识返回值的函数;

调用刚刚创建的函数,即可创建一个生成器

要想使生成器函数得以执行,或者想使执行完 yield 语句立即暂停的程序得以继续执行,有以下 2 种方式:

通过生成器(上面程序中的 num)调用 next() 内置函数或者 next() 方法;

通过 for 循环遍历生成器。

#创建了一个 num 生成器对象。显然,和普通函数不同,intNum() 函数的返回值用的是 yield 关键字,而不是 return 关键字,此类函数又成为生成器函数。
#创建生成器函数
def intNum():
    print("开始执行")
    for i in range(5):
        yield i
        print("继续执行")
#创建生成器对象num
num = intNum()
#调用 next() 内置函数
print(next(num))  #0
#调用 __next__() 方法
print(num.__next__())  #1
#通过for循环遍历生成器
for i in num:  #2 , 3, 4
    print(i)

#输出
开始执行
0
继续执行
1
继续执行
2
继续执行
3
继续执行
4
继续执行

Process finished with exit code 0

除此之外,还可以使用 list() 函数和 tuple() 函数,直接将生成器能生成的所有值存储成列表或者元组的形式

num = intNum()
print(list(num))
num = intNum()
print(tuple(num)

#输出
开始执行
继续执行
继续执行
继续执行
继续执行
继续执行
[0, 1, 2, 3, 4]
开始执行
继续执行
继续执行
继续执行
继续执行
继续执行
(0, 1, 2, 3, 4)

Process finished with exit code 0

参考:

1. C语言中文网

2.Python生成器(Generator)详解

3.Python迭代用法实例教程

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • Python Request类源码实现方法及原理解析

    通过APIView进入找到Request的源码 可以看见一堆属性和方法,其中request.data其实是一个方法,被包装成一个属性 继续看__getattr__和query_params方法: 代码总结: Request其实就是原生request对象被包装后的Request,即重写了__getattr__,return getattr(self._request, attr) 比如:print(request.GET)就当于print(request.query_params) 以上就是本文的

  • Python类super()及私有属性原理解析

    super()有参数写法: # 1.定义父类 class A(object): def __init__(self): self.num = 1 def info_print(self): print(self.num) class C(A): def __init__(self): self.num = 2 def info_print(self): print(self.num) super(C, self).__init__() super(C, self).info_print() #

  • Python类的继承super相关原理解析

    看了网上许多关于super.mro.C3的介绍感觉没有一份很容易初学者理解的文档,直接看C3算法的话,比较难理解,也没必要,如果掌握一套规律的话,会轻松许多.我将网上这些博主的文章进行一个梳理总结,最后形成一套实用的关于super.mro.C3的理解介绍. 1.super super()是一种将子类与父类联系起来的一种方法,子类通过继承父类,可以使用父类的方法和属性,也可以自己定义属于自己的方法和属性.super方法主要用在多继承中,在单继承时直接调用父类方法即可 下面这个是SGD源码的一部分,

  • Python入门变量的定义及类型理解

    变量的定义 在程序中,有时我们需要对2个数据进行求和,那么该怎样做呢? 大家类比一下现实生活中,比如去超市买东西,往往咱们需要一个菜篮子,用来进行存储物品,等到所有的物品都购买完成后,在收银台进行结账即可. 如果在程序中,需要把2个数据,或者多个数据进行求和的话,那么就需要把这些数据先存储起来,然后把它们累加起来即可. 在Python中,存储一个数据,需要一个叫做 变量 的东西,如下示例: num1 = 100 #num1就是一个变量,就像一个小菜篮子 num2 = 87 #num2也是一个变量

  • Python类class参数self原理解析

    1.self只有在类的方法中才会有,其他函数或方法是不必带self的. 2.在调用时不必传入相应的参数. 3.在类的方法中(如__init__),第一参数永远是self,表示创建的类实例本身,而不是类本身. 4.可以把对象的各种属性绑定到self. 5.self代表当前对象的地址.self能避免非限定调用造成的全局变量. 6.self不是python的关键字,也可以用其他名称命名,但是为了规范和便于读者理解,推荐使用self. python中的self等价于C++中的self指针和Java.C#

  • python 类相关概念理解

    目录 什么是类,对象,实例,类## 标题变量(类属性),实例变量(实例属性) 类变量和类属性的分类 类调用实例方法 类的封装(enclosure),继承和多态 迭代,迭代器(iterator),可迭代对象(iterable object),生成器(generator) 总结 什么是类,对象,实例,类## 标题变量(类属性),实例变量(实例属性) 面向对象编程(Object-oriented Programming,简称 OOP),是一种封装代码的方法.比如说,将乱七八糟的数据扔进列表中,这就是一

  • python类继承用法实例分析

    本文实例讲述了python类继承用法.分享给大家供大家参考.具体方法如下: #!/usr/bin/python # Filename: inherit.py class SchoolMember: '''Represents any school member.''' def __init__(self, name, age): self.name = name self.age = age print'(Initialized SchoolMember: %s)'% self.name def

  • Python 类与元类的深度挖掘 I【经验】

    上一篇介绍了 Python 枚举类型的标准库,除了考虑到其实用性,还有一个重要的原因是其实现过程是一个非常好的学习.理解 Python 类与元类的例子.因此接下来两篇就以此为例,深入挖掘 Python 中类与元类背后的机制. 翻开任何一本 Python 教程,你一定可以在某个位置看到下面这两句话: Python 中一切皆为对象(Everything in Python is an object); Python 是一种面向对象编程(Object Oriented Programming, OOP

  • 浅谈python类属性的访问、设置和删除方法

    类属性和对象属性 我们把定义在类中的属性称为类属性,该类的所有对象共享类属性,类属性具有继承性,可以为类动态地添加类属性. 对象在创建完成后还可以为它添加额外的属性,我们把这部分属性称为对象属性,对象属性仅属于该对象,不具有继承性. 类属性和对象属性都会被包含在dir()中,而vars()是仅包含对象属性.vars()跟__dict__是等同的. 类属性和对象属性可类比于Java中的static成员和非static成员,只不python中的类属性和对象属性都是可以动态添加(和删除)的. clas

  • python类的方法属性与方法属性的动态绑定代码详解

    动态语言与静态语言有很多不同,最大的特性之一就是可以实现动态的对类和实例进行修改,在Python中,我们创建了一个类后可以对实例和类绑定心的方法或者属性,实现动态绑定. 最近在学习python,纯粹是自己的兴趣爱好,然而并没有系统地看python编程书籍,觉得上面描述过于繁琐,在网站找了一些学习的网站,发现廖雪峰老师的网站上面的学习资源很不错,而且言简意赅,提取了一些python中的重要的语法和案例.重要的是可以在线测试python的运行代码,缺点就是没有系统的看python的书籍,不能及时的将

  • Python类的继承和多态代码详解

    Python类的继承 在OOP(ObjectOrientedProgramming)程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类.父类或超类(Baseclass.Superclass). 我们先来定义一个classPerson,表示人,定义属性变量name及sex(姓名和性别): 定义一个方法print_title():当sex是male时,printman:当sex是female时,prin

  • python类中super() 的使用解析

    描述 super() 函数是用于调用父类(超类)的一个方法. super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO).重复调用(钻石继承)等种种问题. MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表. 语法 以下是 super() 方法的语法: super(type[, object-or-type]) Python中类的初始化都是__init__(), 所以父类和子类的初始化方式都是__init_

  • Python类中self参数用法详解

    Python编写类的时候,每个函数参数第一个参数都是self,一开始我不管它到底是干嘛的,只知道必须要写上.后来对Python渐渐熟悉了一点,再回头看self的概念,似乎有点弄明白了. 首先明确的是self只有在类的方法中才会有,独立的函数或方法是不必带有self的.self在定义类的方法时是必须有的,虽然在调用时不必传入相应的参数. self名称不是必须的,在python中self不是关键词,你可以定义成a或b或其它名字都可以,但是约定成俗(为了和其他编程语言统一,减少理解难度),不要搞另类,

  • Python类中的装饰器在当前类中的声明与调用详解

    我的Python环境:3.7 在Python类里声明一个装饰器,并在这个类里调用这个装饰器. 代码如下: class Test(): xx = False def __init__(self): pass def test(func): def wrapper(self, *args, **kwargs): print(self.xx) return func(self, *args, **kwargs) return wrapper @test def test_a(self,a,b): pr

  • Python进阶学习之带你探寻Python类的鼻祖-元类

    Python是一门面向对象的语言,所以Python中数字.字符串.列表.集合.字典.函数.类等都是对象. 利用 type() 来查看Python中的各对象类型 In [11]: # 数字 In [12]: type(10) Out[12]: int In [13]: type(3.1415926) Out[13]: float In [14]: # 字符串 In [15]: type('a') Out[15]: str In [16]: type("abc") Out[16]: str

随机推荐