python(五):面向对象--类和实例
一、类的基本概念
类是用来创建数据结构和新类型对象的主要机制。一个类定义了一系列与其实例对象密切关联的属性.典型的属性包括变量(也被称为 类变量)和函数(又被称为方法)。
1、class上下文
class语句用来定义一个类.类的主体语句在类定义同时执行。
class Account(object):
"""一个简单的类"""
print("hello")
account_type = "Basic"
def __init__(self, name, balance):
"""初始化一个新的Account实例"""
self.name = name
self.balance = balance
def deposit(self, amt):
"存款"
self.balance = self.balance + amt
def withdraw(self, amt):
"""取款"""
self.balance = self.balance - amt
def inquiry(self):
"""返回当前余额"""
return self.balance
# 执行上述语句,直接打印hello
在当前Account类中,凡是不含有self的属性和方法,都是直接用Account.属性和Account.方法来访问或执行的。它有些和匿名函数类似。再看下面代码:
class Account(object):
"""一个简单的类"""
print("hello")
account_type = "Basic"
def sayHello():
return "hello" # 直接执行时,会打印hello
print(Account.account_type)
Account.sayHello()
# 打印结果为
# Basic
# 'hello'
结合两个Account类,可以看出:
1.能够直接用对象.属性和对象.方法执行的,都是类属性和类方法;不能执行(含有self参数)的,都是实例对象的属性和方法,需要实例化对象(或者类.方法传入参数实例对象)才能执行。
类方法有两个含义:一是给类定义的,属于类内存空间的方法,如Account.sayHello;二是该方法既然是类对象的方法,就能够被类对象和所有实例对象调用。
class A:
def __init__(self, *args, **kwargs):
self.name, self.age, self.gender = args[:3]
def sayHello(self):
print("my name is %s, %s, %s." % (self.name, self.age, self.gender)) a = A("Li", 27, "male")
A.sayHello(a)
# my name is Li, 27, male.
2.在class中直接写func()(如print),会在代码解析时直接执行,这说明:类属性和类方法、实例方法(如上例account_type)是在类创建时就生成了。跟有没有实例化对象无关。在类中引用一个类的属性必须使用类的全名。
3.代码在解析Account类时,就为类对象开辟了内存空间。而此时还没有实例化对象。
4.类对象作为一个名字空间,存放在类定义语句运行时创建的对象。class语句并不创建类的实例,它用来定义所有实例都应该有的属性。类的名字空间并不是为类主体中的代码(而是实例)服务的。
2、类装饰器--@staticmethod
sayHello()这个函数加上前缀@staticmethod,用以标识它属于这个类(而不是普通函数)的方法,这被称为静态方法。
class AClass(object):
@staticmethod # 静态态方法修饰符,表示下面的方法是一个静态态方法
def astatic():
print('a static method')
anInstance = AClass()
AClass.astatic() # prints: a static method
anInstance.astatic() # prints: a static method
你完全可以将静态方法当成一个用属性引用方式调用的普通函数,静态方法可以直接被类或类实例调用。它没有常规方法那样的特殊行为(绑定、非绑定、默认的第一个参数规则等等)。任何时候定义静态方法都不是必须的(静态方法能实现的功能都可以通过定义一个普通函数来实现)。
3、类装饰器--@classmethod
@classmethod装饰器来装饰的通常称为类方法,并且第一个固定不变的参数是cls,也就是该类对象自身。
class ABase(object):
@classmethod #类方法修饰符
def aclassmet(cls):
print('a class method for', cls.__name__)
class ADeriv(ABase):
pass
bInstance = ABase()
dInstance = ADeriv()
ABase.aclassmet()
bInstance.aclassmet()
ADeriv.aclassmet()
dInstance.aclassmet()
# 打印结果为
# a class method for ABase
# a class method for ABase
# a class method for ADeriv
# a class method for ADeriv
任何时候定义类方法都不是必须的(类方法能实现的功能都可以通过定义一个普通函数来实现,只要这个函数接受一个类对象做为参数就可以了)。避免在类方法中使用了带有self参数的语句,以使cls和self产生混乱,这看起来不伦不类。
4、类装饰器--@property
@property用于将一个实例方法变为属性访问。即调用方式由实例.方法()调用变为实例.方法。
class Goods:
def __init__(self, price, discount):
self.__price = price
self.discount = discount @property
def price(self):
return self.__price * self.discount @price.setter
def price(self, newprice):
self.__price = newprice @price.deleter
def price(self):
del self.__price apple = Goods(20, 0.8)
print(apple.price) #
apple.price = 30 # 看起来像是对self.price重新赋值,但是调用了self.price方法来设置
print(apple.price) #
# del apple.price
5、类的名称空间
所有位于class语句中的代码都在特殊的命名空间中执行--类命名空间(class namespace)。这个命名空间可由类内所有成员访问。
class MemberCounter(object):
members = 0
def init(self):
MemberCounter.members += 1
print(MemberCounter.members) m1 = m2 = m3 = MemberCounter()
m1.init()
m2.init()
m3.init()
# 打印结果为
#
#
#
6、类是如何产生的
当解释器执行到class关键字时,会扫描class上下文的代码语句(类名,类所属类,内容),并交由解释器底层,来通过object实现类的创建。这一过程在class上下文结束时已经完成了。
class_name = "Foo" # 类名
class_parents = (object, ) # 基类
# 类主体
class_body = """
name = "Foo"
def __init__(self, x):
self.x = x
def hello(self):
print("Hello")
"""
class_dict = {}
# 在局部字典class_dict中执行类主体
exec(class_body, globals(), class_dict)
# 创建类对象Foo
Foo = type(class_name, class_parents, class_dict) # type可以指定
Foo("X").hello()
exec和Foo=type()两行模拟了解释器实现类的过程。当我们写class时,解释器会自动查找class_name,class_parents(默认是metaclass=type)和class_body,并在扫描到class上下文结束时,调用type类创建我们写的Foo类。
二、实例的基本概念
1、类和实例的关系
1.类对象通过Class()来实例化对象(如上面的MemberCounter(),即类对象加括号,以此为例)。
class Point:
country = "中国" # 类属性
def __init__(self, x, y):
self.x = x # 实例属性
self.y = y def sayHi(self): # 实例方法
print("hello, i'm {}, {}.".format(self.x, self.y)) point1 = Point("Li", 20)
point1.sayHi() # 对象.方法() 去类内存空间调用类.方法,并将自己传递进去
Point.sayHi(point1) # 类.实例方法(对象)同样可以执行
2.类对象内存空间和实例对象的内存空间是相互独立的,但实例对象保留了对类内存空间的引用和访问。也即类内存空间、类属性和方法能够被其实例化的对象访问。
class MemberCounter:
members = 0
# MemberCount.__init__
# 写__init__(self)只是对MemberCounter.__init__(类对象的特殊方法)的重写
# 不写__init__(self),在实例化对象时直接调用MemberCounter.__init__
# def __init__(self):
# pass
def init(self):
MemberCounter.members += 1
print(MemberCounter.members) m1 = m2 = m3 = MemberCounter() # MemberCount()直接调用了MemberCount.__init__方法来实例化对象
m1.init()
m2.init()
m3.init()
2、实例化的过程
class A(object):
def __init__(self, name, age):
self.name = name
self.age = age
print("__init__ has called.")
def __new__(cls, *args, **kwargs):
print("__new__ has called.")
return object.__new__(cls) a = A("Li", 27)
# __new__ has called.
# __init__ has called.
实例化至少分两个步骤:
1.调用父类的__new__方法来创建一个实例对象。__new__()始终是一个类方法,接受类对象作为第一个参数。尽管__new__()会创建一个实例,但它不会自动地调用__init__()。
如果看到在类中定义了__new__(),通常表明这个类会做两件事之一。
首先,该类可能继承自一个基类,该基类的实例是不变的。如果定义的对象继承自不变的内置类型(如整数、字符串、元组),常常会遇到这种情况,因为__new__()是唯一在创建实例之前执行的方法,也是唯一可以修改值得的地方法。__new__()的另一个主要用途是在定义元类时使用。
class myStr(str):
def __new__(cls, value=""):
u1 = myStr.upper(value)
print(u1)
return str.__new__(cls, value.upper())
@classmethod
def upper(cls, value):
return value.upper()
u2 = myStr("hello")
print(u2) """
HELLO
HELLO """
2.调用自己(或父类)__init__方法来初始化一个实例对象。__init__方法主要用于初始化实例对象的属性,也就是往self.__dict__里添加键值对。在这一过程中,它会调用__setattr__方法。
class MemberCounter:
def __init__(self, name, age):
self.name = name
self.age = age def __setattr__(self, old, new):
print("__setattr__ has called.")
self.__dict__[old] = new # 当这一句被隐藏掉时,会发现打印的字典里没有存储任何值
member = MemberCounter("An", 24)
member.gender = "female"
print(member.__dict__) """
__setattr__ has called.
__setattr__ has called.
__setattr__ has called.
{'name': 'An', 'age': 24, 'gender': 'female'}
"""
3、实例属性
实例对象的主要作用是设置一些key:value,并调用类内存空间中定义好的实例方法来做一些事情。__dict__也是类的特殊方法,用以查看实例对象(或者类对象)的属性。实例对象通常以字典的形式保存属性。
class MemberCounter:
members = 0
def __init__(self, name, age):
self.name = name
self.age = age member = MemberCounter("Li", 27)
print(member.name, member.age) member.gender = "female" # 改
print(member.gender) member.name = "An" # 增
print(member.name)
# print(member.members) # 它在实例内存空间中没找到就会去找父类,找到了就返回;
# member.members = 1000 # 它等价于member.members = 1000;实例不能直接以赋值的方式修改类属性(除非在定义类时用代码规定了可以),因为它调用了__setattr__方法,这个方法只给self赋值
print(member.__dict__) # 查,字典操作 del member.name # 删
print(member.__dict__) # Li 27
# female
# An
# {'name': 'An', 'age': 27, 'gender': 'female'}
# {'age': 27, 'gender': 'female'}
4、实例方法
实例方法的第一个参数必须为self。
class MemberCounter:
members = 0
records = {}
def __init__(self, name, age):
self.name = name
self.age = age
def sayHello(self):
print("Hello, my name is %s, %s." % (self.name, self.age)) member = MemberCounter("Li", 24)
member.sayHello()
5、类和实例的私有属性[数据隐藏]
以__xx格式定义的实例属性和方法被称为私有属性或方法。这样系统会自动生成一个新的名字 _Classname__xx 并用于内部使用。所谓的私有(内部属性),实际上都是公开的。
class Person:
__country = "China"
def __init__(self, name, age):
self.name = name
self.__age = age print(Person.__dict__)
print(Person._Person__country)
print(Person.__dict__["_Person__country"]) Li = Person("Li", 27)
print(Li.__dict__)
print(Li.name)
print(Li._Person__age)
print(Li.__dict__["_Person__age"])
三、3个面试题
1、请说出下面代码打印结果并予以解释
class Foo:
def __init__(self):
self.func()
def func(self):
print('in Foo') class Son(Foo):
def func(self):
print('in son') s = Son()
# in son
解释: 在实例化对象时,如果没有定义__init__方法,则会查找并调用父类中的__init__方法。此时实例对象已经由object.__new__创建,命名为self。于是self.func()会调用s中的func()方法。
2、请说出下面代码打印结果并予以解释
class Foo:
def __init__(self):
self.__func() # self._Foo__func
def __func(self):
print('in Foo') class Son(Foo):
def __func(self): # _Son__func
print('in son') s = Son()
# in Foo
解释: 私有方法和私有属性,在其被访问或执行时,会在当前的class上下文中被强制转化成带有当前classname的新属性。因此,self.__func()在被执行前已被强制转换成self._Foo__func。
print(Foo.__dict__)
"""
{
'__module__': '__main__',
'__init__': <function Foo.__init__ at 0x110523510>,
'_Foo__func': <function Foo.__func at 0x10d022730>,
'__dict__': <attribute '__dict__' of 'Foo' objects>,
'__weakref__': <attribute '__weakref__' of 'Foo' objects>,
'__doc__': None
}
"""
3、请用__new__方法实现单例模式
class Person:
__isinstance = None
def __init__(self, name):
self.name = name
def __new__(cls, *args, **kargs):
if not cls.__isinstance:
obj = object.__new__(cls)
cls.__isinstance = obj
return cls.__isinstance
alex = Person("alex")
egon = Person("egon")
print(id(egon))
print(id(alex))
print(alex.__dict__)
print(egon.__dict__)
"""
4514525024
4514525024
{'name': 'egon'}
{'name': 'egon'}
"""
说明,单例模式只会开辟一个实例内存,不管创建多少个实例,都会覆盖这个内存空间。
四、python3的继承和组合
1、多继承问题
python3的多继承遵循广度优先算法。它会保证每个节点从左到右,从下到上都只访问一次,并找到最近的父类进行继承。所有的节点都必须访问并且都只访问一次。
class A:
def f(self):
print('in A') class B(A):
pass
# def f(self):
# print('in B') class C(A):
pass
# def f(self):
# print('in C') class D(B,C):
pass
# def f(self):
# print('in D') class E(C):
# pass
def f(self):
print('in E') class F(D,E):
pass
# def f(self):
# print('in F') d = D()
d.f()
print(F.mro())
mro
2、super继承
self是子类实例化的对象,在对父类不初始化时,调用父类的实例方法只是"借壳生蛋"。当一个子类实例被创建时, 基类的__init__()方法并不会被自动调用。
super继承:super用来解决python钻石多重继承出现的基类重复调用的问题。在Python3中,直接写super().__init__(*args, **kwargs)。
class B:
varB = 42
def method1(self):
print("Class B : method1")
def method2(self):
return B.varB * 2 class A(B):
varA = 3.3
def method3(self):
print("Class A : method3")
B.method1(self) # 注意,这里的self是A()初始化后的a,B类没有初始化,直接把a当做self传递进去了
return B.method2(self) a = A()
print(a.method3()) """
Class A : method3
Class B : method1
84
"""
class B:
def __init__(self, name, age, *args, **kwargs):
self.name = name
self.age = age
self.salary = 20000
def method1(self):
print("My name is {}, {}, salary {}.".format(self.name, self.age, self.salary)) class A(B):
def __init__(self, *args, **kwargs):
self.name, self.age, self.gender = args
super().__init__(*args, **kwargs) # 第一种继承方法super().__init__(*args, **kwargs)
# B.__init__(self, *args, **kwargs) # 第二种写法 a = A("Li", 27, "male")
a.method1() """
My name is Li, 27, salary 20000.
"""
3、调查继承
如果想要查看一个类是否是另一个类的子类,可以使用内建的issubcalss函数。如果想要知道已知类的基类,可以直接使用它的特殊属性__bases__。
print(issubclass(A, B)) # True
print(A.__bases__) # (<class '__main__.B'>,)
可以使用isinstance方法检查和一个实例对象是否是一个类的实例。使用__class__特性查找一个实例对象属于哪个类。
print(isinstance(a, A)) # True
print(isinstance(a, B)) # True
实例被特殊属性__class__链接回它们的类,所属类名可以用__name.__访问类特殊属性__bases__中将类链接到它们的基类,该属性是一个基类元组。这种底层结构是获取、设置和删除对象属性的所有操作的基础。
class A:
pass
class B(A):
pass b = B() print(b.__class__)
print(b.__class__.__name__)
print(B.__bases__) """
<class '__main__.B'>
B
(<class '__main__.A'>,)
"""
python(五):面向对象--类和实例的更多相关文章
- Python学习(七)面向对象 ——类和实例
Python 面向对象 —— 类和实例 类 虽然 Python 是解释性语言,但是它是面向对象的,能够进行对象编程.至于何为面向对象,在此就不详说了.面向对象程序设计本身就很值得深入学习,如要了解,请 ...
- Python学习:类和实例
Python学习:类和实例 本文作者: 玄魂工作室--热热的蚂蚁 类,在学习面向对象我们可以把类当成一种规范,这个思想就我个人的体会,感觉很重要,除了封装的功能外,类作为一种规范,我们自己可以定制的规 ...
- Python之面向对象类和对象
Python之面向对象类和对象 定义一个类:class 定义类的语法: class Test(object): """ 类里定义一类事物共同的技能. 可以是变量,也可是函 ...
- python中的类和实例
今天花了两个多小时后搜索相关博客看了看python中有关类和实例的介绍,差不多大概明白了. python中的类和c++中的类是一样的,不同之处就是c++的类,如果含有成员变量,并且成员变量发生变化后, ...
- Python学习 Day 7 面向对象 类和实例 访问限制
面向对象编程 面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想.OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数. 面向过程的程 ...
- Python面向对象-类、实例的绑定属性、绑定方法和__slots__
绑定属性 从之前的文章中,我们知道python是动态语言——实例可以绑定任意属性. 那如果实例绑定的属性和类的属性名一样的话,会是什么情况呢? >>> class Student(o ...
- Python进阶_类与实例
上一节将到面对对象必须先抽象模型,之后直接利用模型.这一节我们来具体理解一下这句话的意思. 面对对象最重要的概念就是类(class)和实例(instance),必须牢记类是抽象的模板,比如studen ...
- Python面试题之Python中的类和实例
0x00 前言 类,在学习面向对象我们可以把类当成一种规范,这个思想就我个人的体会,感觉很重要,除了封装的功能外,类作为一种规范,我们自己可以定制的规范,从这个角度来看,在以后我们学习设计模式的时候, ...
- python基础编程——类和实例
在了解类和实例之前,需要先了解什么是面向对象,什么又是面向过程.面向过程是以过程为中心实现一步步操作(相互调用,类似流水线思想):面向对象是以事物为中心,某个事物可以拥有自己的多个行为,而另一个事物也 ...
随机推荐
- Scrapy:学习笔记(1)——XPath
Scrapy:学习笔记(1)——XPath 1.快速开始 XPath是一种可以快速在HTML文档中选择并抽取元素.属性和文本的方法. 在Chrome,打开开发者工具,可以使用$x工具函数来使用XPat ...
- supervisor安装及其配置
一.supervisor概述 supervisor是一个c/s系统,被用来在类Unix系统中监控进程状态.supervisor使用python开发. 服务端进程为supervisord,主要负责启动自 ...
- 20155305乔磊2016-2017-2《Java程序设计》第九周学习总结
20155305 2016-2017-2 <Java程序设计>第九周学习总结 教材学习内容总结 JDBC入门 JDBC简介 1.JDBC是java联机数据库的标准规范,它定义了一组标准类与 ...
- Entity Framework 复杂类型(转)
为了说明什么是复杂属性,先举一个例子. public class CompanyAddress { public int ID { get; set; } public string CompanyN ...
- Digital Image Processing 学习笔记1
第一章 1.1 数字图像 一幅图像可以定义为一个而为函数, 其中x和y是空间坐标,而在任何一对空间坐标(x, y)处的幅值f称为图像在该点处的强度或灰度.当x, y和灰度值f是有限的离散数值时,该图像 ...
- Linux学习笔记之Linux计划任务Crontab
0x00 cron 简介 cron 是 UNIX, SOLARIS,LINUX 下的一个十分有用的工具.通过 cron 脚本能使计划任务定期地在系统后台自动运行. 0x01 cron 命令 cront ...
- Python3基础 print \" 输出单引号与双引号
Python : 3.7.0 OS : Ubuntu 18.04.1 LTS IDE : PyCharm 2018.2.4 Conda ...
- git squash 和 git rebase
In git, what is the difference between merge --squash and rebase? 上面链接的回答中的总结: Both git merge --squa ...
- JavaScript权威指南--window对象
知识要点 window对象及其客户端javascript所扮演的核心角色:它是客户端javascript程序的全局对象.本章介绍window对象的属性和方法,这些属性定义了不同的API,但是只有一部分 ...
- 【整理】STL中的bitset(二进制华丽解决假五维偏序题)
------------更多Bitset的运用,请看这里http://www.cnblogs.com/hua-dong/p/8519739.html. 由于在学cdq分治,看到了这道题.先来看一道题目 ...