起名Python基础4

本节内容

一.对于部分合1起来的通用配置表,如上面那样的:

 1.迭代器 &
生成器

起名 1

 2.装饰器

要么上海教室吧

 三.Json & pickle
数据体系化

那种通用表,字段直接是value一,value贰,value3,value四,value五,value陆。

 四.软件目录结构正式

那是GameConfigData里面包车型客车类:

 

   class CGodWingJinJieBase
        :public cdf::CRefShared
    {
    public:
        Message::Db::Tables::TGodWingConfig _godWingJinJieConfig;
    };
    typedef cdf::CHandle<CGodWingJinJieBase> GodWingJinJieBasePtr;

壹、列表生成式, 迭代器 &
生成器

那是应用的代码:

列表生成式

       if(!CGodWingConfigManager::instance()->getJinJieInfoConfig(playerGodWing.jinJieLevel, godWingJinJieConfig))
        {
            CErrorCodeManager::throwException("Public_GameConfigDataError");
        }

        //判断是否已拥有该皮肤
        if(!godWingManager->isSkinExist(godWingJinJieConfig.value6, true))
        {
            //添加皮肤
            godWingManager->addGodWingSkin(godWingJinJieConfig.value6, "", gateEntity, player);

            //设置皮肤
            godWingManager->setGodWingSkin(godWingJinJieConfig.value6, player, gateEntity);
        }

首先, 看列表[0, 1, 2,
3, 4, 5, 6, 7, 8, 9], 未来让列表里面包车型大巴各样值都加一,
怎么着完毕?

那样直接使用value六,以后各样人见状此间的代码,都要去数据库查一下,还得对照一下别样代码。万分困苦。

a = [0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9]
b = []
for i in a:b.append(i+1)
a = b
>>>>>>>>a的结果:
[1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 10]

a = [0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9]
for index,i in enumerate(a):
    a[index]+=1
>>>>>>a 的结果为:
[1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 10]

a = [0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9]
a = map(lambda x:x+1, a)
print(a)
for i in a:
    print(i)
>>>>>>输出:
<map object at 0x0000021EA94291D0>
1
2
3
4
5
6
7
8
8
9
10

在GameConfigData读取的时候应该进行李包裹装,转换到可读取的字段,而不是一向利用。

再有种写法如下:

修改后:

a = [i + 1 for i in range (10)]
print(a)
>>>>>>输出:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
class CGodWingJinJieConfig
        :public cdf::CRefShared
    {
    public:
        int jie;//阶
        int jiePropNum;//消耗进阶道具数量
        int blessValue;//祝福值
        int attributeId;//属性
        int skillCode;//技能
        int skinCode;//皮肤 
    };
    typedef cdf::CHandle<CGodWingJinJieConfig> CGodWingJinJieConfigPtr; 
    typedef std::map<int, CGodWingJinJieConfigPtr> MapCGodWingJinJieConfig;

Message::Db::Tables::SeqTGodWingConfig::const_iterator iter1 = tGodWingConfigs.begin();
    for(; iter1 != tGodWingConfigs.end(); iter1++)
    {
        switch(iter1->type)
        {
        case 1://神翼升星的经验
            {
                _starExpMap[iter1->value1][iter1->value2] = iter1->value3;
            }
            break;

        case 2://阶的数据
            {
                CGodWingJinJieConfigPtr jinJenConfig = new CGodWingJinJieConfig();
                jinJenConfig->jie = iter1->value1;
                jinJenConfig->jiePropNum = iter1->value2;
                jinJenConfig->blessValue = iter1->value3;
                jinJenConfig->attributeId = iter1->value4;
                jinJenConfig->skillCode = iter1->value5;
                jinJenConfig->skinCode = iter1->value6;
                _jinJieMap[iter1->value1] = jinJenConfig;
            }
            break;

地点的例证就叫做列表生成.

这么取出来的布置,外人能够平素运用。

 

做游戏的代码,一定要随时准备随时令人家看,因为每3日要改。

生成器

贰.typedef也正是再次注脚了3个组织,所以起初应该大写。

因此列表生成式,就可以直接创立一个列表。不过,受到内部存款和储蓄器限制,列表体积肯定是有限的。而且,成立二个富含100万个要素的列表,不仅占用十分大的内存存款和储蓄空间,假诺大家只有须要拜访后边多少个因素,那背后绝大部分因素占用的上空都白白浪费了。

typedef std::map<int, GodWingJinJieConfigPtr> mapCGodWingJinJieInfoList;
typedef std::map<int, GodWingExpSkinPtr> mapCGodWingSkinInfoList;
typedef std::map<int, GodWingSkillPtr> mapCGodWingSkillInfoList;
typedef std::map<int, Message::Game::SPlayerGodWingCritPrompt> mapJinJieExpInfoList;

由此,若是列表成分得以依照某种算法推算出来,那我们是否足以在循环的长河中不止推算出后续的因素呢?那样就无须成立完整的list,从而省去多量的空间。在Python中,那种单方面循环1边盘算的体制,称为生成器(generator)。

始于的m都应该改成M.

 

除此以外,对于那种小名,最棒起名字的时候不要带有具体效果意思的名字,终究其余地方能够采用的。

要创造3个generator,有很三种措施。第三种艺术很简短,只要把一个列表生成式的[]改成(),就创办了三个generator:

三.对此拼音的命名,1个词的发端才大写。是词,不是字。

a = [i + 1 for i in range (10)]
>>>>>>>>>>
b = (i + 1 for i in range (10))
print(b)
>>>>>>>>>>输出结果为:
<generator object <genexpr> at 0x0000028FFC57FFC0>
GodWingJinJieConfigPtr

成立a, b的界别仅在于最外层的[] 和(),
a是一个list,b是一个generator.

应该是

能够一贯打字与印刷出list a的要素,
那如何打字与印刷出生成器generator b里面包车型大巴元素呢?

GodWingJinjieConfigPtr

比方想要1个个打字与印刷出来的话,
能够由此带哦用next()函数来取得下二个再次回到值:

1经要用拼音的话。

>>> a = (x * x for x in range(10))
>>> next(a)
0
>>> next(a)
1
>>> next(a)
4
>>> next(a)
9
>>> next(a)
16
>>> next(a)
25
>>> next(a)
36
>>> next(a)
49
>>> next(a)
64
>>> next(a)
81
>>> next(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

肆.除数无法为0

generator保存的莫过于是算法,每一回的调用next(a),
就计算出a的下1个值, 知道总括到最终一个成分, 未有更加多的成分时,
抛出StopIteration的报错。

bool CGodWingConfigManager::getBlessingValue(int jinJieLevel, int blessingValue, bool& isLevelUp, Message::Game::SPlayerGodWingCritPrompt& cirtInfo)
{
    MapCGodWingJinjieConfig::const_iterator iter1 = _jinJieMap.find(jinJieLevel);
    if(_jinJieMap.end() == iter1)
    {
        return false;
    }
    if (iter1->second->blessValue <= 0)
    {
        return false;//这里是我加的。
    }
    //获取当前经验百分比
    int blessPercent = blessingValue * 10000 / iter1->second->blessValue;//load配置的地方没检测,函数内也没有检测

    //判断是否可以直接进阶
    int rand = Common::CUtil::myRand(1, 10000);
    for(DictIntInt::const_reverse_iterator iter2 = _jinjieLevelUpMap.rbegin();
        _jinjieLevelUpMap.rend() != iter2;
        ++iter2)
    {

本来, 如上的持续调用实在是太为难了,
正确的不贰诀固然利用for循环,因为generator也是可迭代对象:

5.函数不要用大写伊始,类名,结构名都要用大写开始。属性不要用小写。

>>> a = (x * x for x in range(10))
>>> for i in a:
...     print(i)
...
0
1
4
9
16
25
36
49
64
81
interface IGodWing
        {
            /*
            * 神翼培养接口
            * @param type 1激活神翼系统,2神翼加经验
            * @param amount 使用数量
            * @exception --失败返回异常 使用异常代码
            */
            ["ami","amd"] void GodWingLevelUp(int type, int amount);//大写了

            /*
            * 神翼进阶接口
            * @param autoBuy 是否自动买 0不自动,1自动
            * @param result 返回进阶结果 1进阶成功,2进阶失败
            * @param jinJieValue 返回进阶值
            * @exception --失败返回异常 使用异常代码
            */
            ["ami","amd"] void GodWingJinJie(int autoBuy, out int result, out int jinJieValue);//大写了

            /*
            * 打开神翼系统接口
            */
            ["ami","amd"] void OpenGodWing();//大写了


            /*
            * 领取返还进阶石
            */
            ["ami","amd"] void getJinJieReward();

创办一个生成器后, 一般不会调用next,
而是用for循环来迭代它, 并且不要求关心StopIteration的错误。

甚至还写到cdl里面了。。。。。。

generator极度强大。如若推算的算法相比复杂,用接近列表生成式的for循环不能完成的时候,还足以用函数来贯彻。

陆.封存数据库的时候如非须求,都以延时保存。immediatelyUpdate是即时更新的意思,壹般选用changeFlag。

譬如,盛名的斐波拉契数列,除了第三个和第二个数外,任意2个数都以前3个数的和:

//保存数据库
    getSaveInfo(ETPlayerGodWing).immediatelyUpdate = true;
    save(false);

1, 1, 2, 3, 5, 8, 13, 21, 34,
……

7.选择json,有补益也有弊端。好处是比较有利,节省字段。

斐波拉契数列用列表生成式写不出去,
但是用函数是能够消除的:

可是坏处也很强烈,

def fib(max):
    n, a, b = 0, 0, 1
    while n < max :
        print(b)
        a, b = b, a + b
        n += 1
    return 'done'

fib(10)
>>>>>>>
1
1
2
3
5
8
13
21
34
55

一.多了许多附加的运算

留神上边的 a, b = b, a + b

二.庞大扩展调试的时候查看内部存款和储蓄器

并不是想象中的:

三.因为不要求证明,所以也就不亮堂具体有微微字段在里头。

a = b
b = a+b

据此1般景观下,

而是一定于:

一.json只是用来节省字段,保存数据库的时候才转化一下。

t = (b, a + b)# t是一个tuple
a = t[0]
b = t[1]

二.对于部分大的种类json的key值放在Common/Config中

但不必呈现的写出一时变量t就能够赋值。

三.对于部分小的地点,只是简短四个字符串就行。

能够见到,
fib函数实际上是概念了斐波拉契的推算规则, 从而获得继续成分,
跟generator的逻辑很相似。

肆.不保留复杂的构造,太复杂的要驰念新建贰个表。

实则上边的函数跟generator仅一步之遥。只需求把print(b)改成yield(b)就能够转化了。

五.倘使实在要保存复杂的内容,先转成string,然后保留到json中,登6的时候,转成各自的结构,要保留的时候才转成字符串。

def fib(max):
    n, a, b = 0, 0, 1

    while n < max:
        yield b
        a, b = b, a + b
        n +=1
    return 'done'
f = fib(5)
print(f)

陆.key中要写明是哪些数据类型,value一般只是基础数据类型。

出口结果:<generator object fib at
0x0一D87180>

总计得不得了,其实我觉着最大标题是,根本没人知道你里面保存的是怎样。

generator跟函数的运行流程不雷同。韩式是各类执行,境遇return语句也许最后一行函数语句就回去。而改为生成器后通过next()调用,蒙受yield语句重回,再度实施从上次赶回的yield语句继续执行。

bool GateApp::CGodWingManager::isSkinExist(int skinCode, int flag)
{
    Json::Value json;
    json.parse(_playerGodWing.skinJsStr);

    if(flag)
    {
        for(Json::Value::iterator iter = json[ALL_SKIN_KEY].begin(); json[ALL_SKIN_KEY].end() != iter; ++iter)
        {
            if((*iter).isInt())
            {
                if(skinCode == (*iter).asInt())
                {
                    return true;
                }
            }
        }
    }
    else
    {
        for(Json::Value::iterator iter = json[LIMIT_SKIN_KEY].begin(); json[LIMIT_SKIN_KEY].end() != iter; ++iter)
        {
            if(!iter.key().isNull())
            {
                std::string temp = iter.key().asString();
                if(skinCode == atoi(temp.c_str()))
                {
                    return true;
                }
            }
        }
    }

    return false;
}
f = fib(5)
print(f)
print(f.__next__())
print(f.__next__())
print("做点其他的事")
print(f.__next__())
print(f.__next__())

<generator object fib at 0x00A27180>
1
1
做点其他的事
2
3

像那种未来绝不再用了。

在上头的fib的例证中,大家在循环进度中穿梭调用yield,就会不断中断。当然要给循环设计二个退出标准,否则会生出三个格外数列。

8.playerExtend和playerExtend2保存

实则,同样的,
函数改为generator后,大家也能够用for循环来迭代而不是next调用。

   CPlayerHelper::updatePlayerExtend(gateEntity, player);//用这个
    player->getSaveInfo(ETPlayerExtend).changeFlag = true;//不要这样写
f = fib(5)
for i in f:
    print(i)

1
1
2
3
5

九.温馨写的效率,要每一句代码都调节和测试过才行。

调用for循环后,会发现不能够取得函数重临值,假设想获得它,必须捕获StopIteration错误,再次回到值包蕴在StopIteration的value中:

void GateApp::CItemExtend::useProp( CGateEntityPtr gateEntity, 
    CPlayerPtr player,
    ::GateApp::Bag::CBagPtr bag,
    const CPlayerItemPtr& playerItem,
    Common::CBaseItemPtr item,
    int useAmount,
    const ::Message::Public::SeqString& values
                                   )
{

    switch( item->getTItem().type )
    {

    case EPropMountDan:
        {
            CItemExtend::useMountAttributeDan(gateEntity, bag, playerItem, item, useAmount);
        }
        break;
    case EPropGodWingDan:
        {
            CItemExtend::useMountAttributeDan(gateEntity, bag, playerItem, item, useAmount);//这里明显是坐骑的丹药。。。
        }
        break; 
f = fib(5)
while True:
    try:
        x = next(f)
        print('F:',x)
    except StopIteration as e:
        print("Generator return value:", e.value)
        break

结果:
F: 1
F: 1
F: 2
F: 3
F: 5
Generator return value: done

大家做游戏的流程,测试职员只是负责做黑盒测试,因为非技术人士的职工,关切点一般都在客户端。而服务端的bug除非很醒目,当然那几个是很显眼的。不然是测不到的,很多气象下要靠本人的编码习惯来规避一些起码错误。

 还可经过yield达成在单线程的景色降低成并发运算的效率  

十.货品价位必须从t_shop_shell中获得,

#!/user/bin/env python
# -*- coding: utf-8 -*-

import time


def consumer(name):
    print("%s准备吃包子啦!" % name)
    while True:
        baozi =  yield
        print("包子[%s]来了,被[%s]吃了!"%(baozi, name))


def producer():
    c1 = consumer('Dandy')
    c2 = consumer('Claire')
    c1.__next__()
    c2.__next__()
    print(' 师傅开始准备做包子啦!')
    for i in range(10):
        time.sleep(1)
        print('做了一个包子,分2半。')
        c1.send(i)
        c2.send(i)

producer()

Dandy准备吃包子啦!
Claire准备吃包子啦!
 师傅开始准备做包子啦!
做了一个包子,分2半。
包子[0]来了,被[Dandy]吃了!
包子[0]来了,被[Claire]吃了!
做了一个包子,分2半。
包子[1]来了,被[Dandy]吃了!
包子[1]来了,被[Claire]吃了!
做了一个包子,分2半。
包子[2]来了,被[Dandy]吃了!
包子[2]来了,被[Claire]吃了!
做了一个包子,分2半。
包子[3]来了,被[Dandy]吃了!
包子[3]来了,被[Claire]吃了! 
           //判断元宝是否足够
            player->enoughMoneyException(EPriceUnitEMoney, cost * oweAmount, updateCode);//商品价格和单位必须从t_shop_shell中获得,

            //购买道具
            CShopManager::instance()->buyItem(gateEntity, SHOP_CODE_AUTO_BUY, itemCode, oweAmount, updateCode);//这里根本不需要购买道具,而是在下面直接扣钱

 

不能协调找个地方布署。

迭代器  

使用商品的时候必须使用t_shop_shell的unit和amount。

大家已经知晓,能够平素效果于for销魂还得额数据类型有以下二种:一类是聚众数据类型,入list,
list, dict, set, str等。另壹类是generator,
包罗生成器和带yield的generator function。

公司优惠活动必定会有的。这一个必必要注意。

上述能够一直效果于for循环的对象,统1称为可迭代对象:Iterable。

1壹.对此bool的情节,做判定的时候绝不再==true恐怕==false了。

能够用isinstance()判断叁个指标是还是不是是Iterable对象。

   tlogGodWing.oldLevel = (true == flag) ? playerGodWing.jinJieLevel - 1 : playerGodWing.jinJieLevel;
    tlogGodWing.newLevel = playerGodWing.jinJieLevel;
    tlogGodWing.newExp = playerGodWing.blessingValue + playerGodWing.limitBlessingValue;
    tlogGodWing.oldExp = oldBless;
    tlogGodWing.addExp = (true == isLevelUp) ? totalExp - oldBless : exp;
>>> from collections import Iterable
>>> isinstance([],Iterable)
True
>>> isinstance({},Iterable)
True
>>> isinstance('abc',Iterable)
True
>>> isinstance((x for x in range(10)),Iterable)
True
>>> isinstance(100,Iterable)
False

1二.在壹般的损耗某种道具,然后拿走某种属性的流程中,要先判断全部希望抛错误码的事态,然后再去执行扣除道具,最后是收获东西。

 生成器都以Iterator对象,不过list,dict,str尽管是Iterable,但却不是Iterator。

按实际的事例来说呢:

把list,dict,str等Iterable变成Iterator可以用iter()函数:

在神翼进阶的流水生产线中,先判断了是不是有近日配备,然后依据道具为空判断是还是不是为最大阶数。其实是没要求的。下三个阶的布局是无论怎么样都要收获的。

>>> isinstance(iter([]),Iterator)
True
>>> isinstance(iter('abc'),Iterator)
True
>>>

你可能会困惑,为什么list,dict,str等不是Iterator?
这是因为python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据抛出StopIteration。可以把这个数据流看成是一个有序的序列,
但我们却不能提前知道序列的长度,只能不断的通过next()的调用实现计算出下一个数,所以Iterator的计算是惰性的,自由在需要返回下一个数据时才会计算。

Iterator甚至可以表示一个无限大的数据流,但是list等却永远不可能存储整体的自然数。
   //获取进阶信息
    CGodWingJinjieConfigPtr jinjieConfig = CGodWingConfigManager::instance()->getJinJieConfig(playerGodWing.jinJieLevel); 
    if (!jinjieConfig)
    {
        CErrorCodeManager::throwException("Public_GameConfigDataError");
    }

    //判断是否最高阶数
    if (0 == jinjieConfig->jiePropNum)//不消耗道具,是最大阶
    {
        CErrorCodeManager::throwException("Public_jinJieMaxLevel");
    }

这是后面,消耗道具之后,再去获取下一阶的配置。如果这里出现问题,之前计算的内容,都白费了,而且还白扣钱了。

 CGodWingJinjieConfigPtr nextJinjieConfig = CGodWingConfigManager::instance()->getJinJieConfig(playerGodWing.jinJieLevel + 1);
    if (!nextJinjieConfig)
    {
        CErrorCodeManager::throwException("Public_jinJieMaxLevel");
    }

小结

此处能够思考三个难题。在程序运维进程中,非常小可能率出现意外的动静,是先扣除玩家道具呢?照旧先给玩家属性?

大凡可职能于for循环的指标都以Iterable类型(可迭代对象);

按理说说,玩家处于弱势,扣了钱,不给属性,非凡亏,1怒之下恐怕都不想玩了。好像我们应超越行为玩家思量的规范。

大凡可职能于next()函数的靶子都以Iterator类型,它意味着二个惰性计算的行列;

唯独事实上意况是,市面上存在各类各个的外挂以及各式种种想占便宜的玩家。假诺玩家发现了bug,而以此bug对团结有利,能够不劳而获,尽管是十分的小可能率,他们也会去商讨,将那种独特别情报况出现的可能率大大提升,甚至每一遍都出现。假如是玩家自个儿意识的幸亏,只怕他协调暗中刷bug,大家发现了就处理了,没察觉也只是震慑三个服的标题。可是壹旦是被外挂利用了,恐怕在极长时间内,被很四个人疯狂刷,那一年很可能大家就得回档,就算没那样惨,也得各样补偿,对娱乐的生命周期大大影响。而且10分打击通常玩家的信心。

汇合数据类型例如
list,dict,str等都是Iterable但不是Iterator,然而能够通过iter()函数得到多个Iterator对象。

说多了,综上说述是先扣钱,再给属性。

Python
的for循环其实本质上可以说,正是3个相接的调用next()函数的历程,例如

在写代码的时候要有察觉,先把只怕抛错的放前边,到了扣物品扣钱的那一步开端,平常就不能够再去抛错了。

for x in [1, 2, 3, 4]:
    pass

一三.依旧那一句,起名是那些首要的,关乎后来者能还是不可能正确看你的代码。

 完全等价于:

那是神翼进阶的代码,同多少个函数内的。

# 首先获得Iterator对象
it = iter([1, 2, 3, 4])
#循环
while True:
    try:
        #获取下一个值:
        x = next(it)
    except StopIteration:
        #遇到StopIteration就退出循环
        break
int totalExp = jinjieConfig->blessValue; 
int totalexp = playerGodWing.blessingValue + playerGodWing.limitBlessingValue;

 

1四.绝不在七个讲话里面做太多的预计。

二、装饰器

事实上自个儿是想说三目运算符?:

仿照1种情状,倘使现在您手上的网址未来有以下多少个模块

tlogGodWing.addExp = ( isLevelUp) ? totalExp - oldBless : exp;
def home():
    print("---首页----")

def america():
    print("----欧美专区----")

def japan():
    print("----日韩专区----")

def dalu():
    print("----大陆专区----")

并发那种景色,日常都以地点已经分成很多拨出了,定义个addExp,在个中复制就好了。能够代码尤其清楚。

今后亟待对那一个受欢迎的版块进行改版,比如大6跟日韩专区要求收费,得到那一个要求后,先要让其进展用户认证,认证通过后再盘点是还是不是VIP,付费会员就足以看到。须要实际上很简短,因为必要对多个板块进行验证,就需求写八个独门并且通用的模块,然后各个版块调用它。

一5.客户端调用接口的时候,一些不是很关键的代码,能够放在Response之后。

#!/user/bin/env python
# -*- coding: utf-8 -*-

user_status = False


def login():
    _username = 'dandy'  # 假装是DB抓取的用户信息
    _password = 'password.1'
    global user_status

    if user_status == False:
        username = input("user:")
        password = input("password:")

        if username == _username and password == _password:
            print("Welcome login...")
            user_status = True
        else:
            print("Wrong username or password !")
    else:
        print("用户已登陆,验证通过...")


def home():
    print("---首页----")


def america():
    print("----欧美专区----")


def japan():
    login()#验证
    print("----日韩专区----")


def dalu():
    login()#验证
    print("----大陆专区----")
   GodWingJinJieCB->cdeResponse(isLevelUp, exp);

    //for log
    Message::Db::Tables::TLogGodWing tlogGodWing;
    tlogGodWing.__init();
    tlogGodWing.userName = player->getTPlayer().username;

 那时候你只怕信心满满的去付出审查,没悟出几分钟就被打回来。固然代码是贯彻了职能,然则供给加认证在各样模块代码,这一向违反了软件开发的1个标准“开放-封闭”,一言以蔽之,就是规定了曾经落到实处的意义代码差异意被涂改,但能够增加,即:

就像是那样,日志的保存,放在response之后。

· 封闭:已落到实处的功效代码

16.bool不要和int混用

· 开放:对扩展开放

       //判断皮肤是否已拥有,flag为true检测永久卡中是否已拥有该卡,false检测限时卡中是否已拥有该卡
        bool isSkinExist(int skinCode, int flag);

此时大家能够选用高阶函数,把2个函数当作多少个参数字传送给别的一个函数。那样只供给写2个阐明格局,每一回调用供给证实的时候一直把效果的函数名当作参数字传送给须求表明的模块,就足以兑现效益了。

相似情况下是不会有反常态的,不过只要出题指标时候,只能呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵呵了,碰着过,万分难找。尽量不要用私下认可类型转换。这一定于削弱了C++强类型的性子,变成弱类型,就会像客户端的Lua1样,分外多bug,而且很难修的。

起名 2起名 3

一7.检查测试限时的皮层的时候,要咬定一下时日是不是过期了。尽管一度有每分钟检查评定的定时器,不过由于谨慎,依然要检查评定一下。

 1 #!/user/bin/env python
 2 # -*- coding: utf-8 -*-
 3 
 4 user_status = False
 5 
 6 
 7 def login(func):
 8     _username = 'dandy'  # 假装是DB抓取的用户信息
 9     _password = 'password.1'
10     global user_status
11 
12     if user_status == False:
13         username = input("user:")
14         password = input("password:")
15 
16         if username == _username and password == _password:
17             print("Welcome login...")
18             user_status = True
19         else:
20             print("Wrong username or password !")
21 
22     if user_status == True:
23         func()#只要验证通过就会调用相应的功能模块
24 
25 
26 def home():
27     print("---首页----")
28 
29 
30 def america():
31     print("----欧美专区----")
32 
33 
34 def japan():
35     # login()#验证
36     print("----日韩专区----")
37 
38 
39 def dalu():
40     # login()#验证
41     print("----大陆专区----")
42 
43 home()
44 login(america)
45 login(dalu)
bool GateApp::CGodWingManager::isSkinExist(int skinCode, bool isForever)
{ 
    if (isForever)
    {
        for (SeqInt::iterator iter = _foreverSkins.begin();
            iter != _foreverSkins.end();
            iter ++)
        {
            if (skinCode == *iter)
            {
                return true;
            }
        } 
    }
    else
    {
        DictIntInt::iterator iter = _limitSkinMap.find(skinCode);
        if (iter != _limitSkinMap.end())
        {
            cdf::CDateTime now;
            if (iter->second > now.getTotalSecond())//-----这个检测是我加的,本来没有的----------------
            {
                return true;//还没过期
            }
        } 
    }

    return false;
}

View Code

18.对于穿戴皮肤,传0过来就是脱掉了。没要求弄多二个接口。语义上也是同等的,分出来很多余。

相当热情洋溢,美滋滋。终于达成了业主的渴求,不更改原效劳代码的前提下,给功效丰盛验证。然则此时老董看来后发现你又犯了避忌,什么禁忌?

void
::Message::Game::IGodWingImpl::setGodWingSkin_async(const ::Message::Game::AMD_IGodWing_setGodWingSkinPtr& setGodWingSkinCB,
int skinCode,
const ::cde::CContext& context)

更改了调用格局!未来怀有需求阐明的模块都要求再行调用你的login()方法并把团结的函数名传给你,外人在此以前可不是这么调用的,若是有玖1八个模块那样调用是还是不是玖拾玖个模块都要改成调用方式,或许模块跟措施是分别封装的,怎么去找那多少个模块呢?

上边那么些是剩下的。

前日让大家提及来二个学过的文化:匿名函数(lambda)

void
::Message::Game::IGodWingImpl::takeOffGodWingSkin_async(const ::Message::Game::AMD_IGodWing_takeOffGodWingSkinPtr& takeOffGodWingSkinCB,
const ::cde::CContext& context)
def plus(n):
    return n+2

plus2 = lambda x:x+2

1玖.某个函数里面会抛非凡,在外场就不要再去抛了。

地点的二种写法是还是不是象征壹律的趣味?

GateApp::Bag::CBagPtr roleBag = CBagHelper::getBag(gateEntity, EPlayerItemPosTypeRole);
if(!roleBag)
{
    CErrorCodeManager::throwException("ErrorGate_BagNotExist");
}

给 lambda x:x+二 取了个名字叫plus2 ,
是还是不是一对1于def plus二(x)

实际函数里面已经抛很是了。。。

给函数赋值变量名就像是def func_name
是相同的功用,如上面包车型地铁plus(n)函数,你调用时方可用plus名,还能再起个其余名字,如

GateApp::Bag::CBagPtr GateApp::CBagHelper::getBag( 
    const CGateEntityPtr& gateEntity,
    const ::Message::Public::EPlayerItemPosType& type, 
    bool isException /*= true*/ )
{
    ::GateApp::Bag::CBagSystemPtr bagSystem = getBagSystem(gateEntity, isException);
    if (bagSystem == NULL)
    {
        return NULL;
    }

    GateApp::Bag::CBagPtr bag = bagSystem->getBag( type );
    if ( ! bag && isException )
    {
        Common::CErrorCodeManager::throwException( "ErrorGate_BagNotExist" );
    }
    return bag;
}
calc = plus

calc(n)

20.对于1些质量,过了某些时刻失效的图景。设个定时器,到了这一个时间点,就把拥有的都改掉。那是谋划的讨论,而不是程序员的做法。

后边的表明登六的代码:

int CPlayerGodWingTimer::handleTimeout(const cdf::CDateTime ¤tTime, const void *act)
{

    CGateEntityPtr gateEntity = CGateEntityManager::instance()->findGateEntity( _entityId );
    if (!gateEntity)
    {
        return 0;
    }

    CPlayerPtr player = CPlayerPtr::dynamicCast(gateEntity->getComponent(ECOMPONENT_TYPE_PLAYER));
    if(!player)
    {
        return 0;
    }

    CGodWingManagerPtr godWingManager = CGodWingManagerPtr::dynamicCast(gateEntity->getComponent(ECOMPONENT_TYPE_PLAYER_GODWING));
    if(!godWingManager)
    {
        return 0;
    }

    //等级判断
    if (player->isFunctionOpen("GodWing"))
    {
        //清空限时祝福值
        godWingManager->checkLimitBless(player);
    }

    gateEntity->removeTimer(ETIMER_TYPE_PLAYER_GOD_WING_TIMER);

    CPlayerGodWingTimerPtr timer = new CPlayerGodWingTimer();
    timer->_entityId = _entityId;
    cdf::CReactor::instance()->schedule(timer, NULL,
        cdf::CInterval(1, 5, 0, 0, 0), 0);
    gateEntity->addTimer(ETIMER_TYPE_PLAYER_GOD_WING_TIMER, timer);

    return 0;
}
home()
login(japan)
login(dalu)

定时器是个很便宜的事物,可是用多了可能造成负担。而且那种限制时间,对富有玩家都使得的气象。恐怕会造成集中时间产生多量盘算。那一个时刻会很卡。

故此改变了调用方式,是因为用户每趟调用时索要实施login(参数),
能够稍微改一下。

再正是五点钟,应该是很少人的时候,系统1般都实行部分比较耗费时间的操作,更新进步维护备份什么的。说多了,就是尽量不要用定时器。

home()
japan= login(japan)
dalu = login(dalu)

正确的做法是在运用的时候,检查实验一下,发现时间过时了,就创新一下。

诸如此类调用dalu的时候,其实一定于调用了login(dalu),通过login里面包车型大巴验证后自动调用dalu的效应。

void
::Message::Game::IGodWingImpl::godWingJinJie_async(const ::Message::Game::AMD_IGodWing_godWingJinJiePtr& GodWingJinJieCB,
    int autoBuy,
    const ::cde::CContext& context)
{
    CGateEntityPtr gateEntity;
    CPlayerPtr player;
    CGateHelper::getPlayerAndEntity(context, gateEntity, player);

    //参数检查
    CCheckInput::validValue(autoBuy, 0, 1);

    CGodWingManagerPtr godWingManager = CGodWingManagerPtr::dynamicCast(gateEntity->getComponent(ECOMPONENT_TYPE_PLAYER_GODWING));
    if(!godWingManager)
    {
        CErrorCodeManager::throwException("Public_NullPointer");
    }
    godWingManager->checkLimitBless(player);

那就相应那样写

makeClientGodWingInfo函数里面也检验一下就够了。

home()
japan= login(japan)#这里其实替换了japan函数
dalu= login(dalu)

#那用户调用时依然写
japan()

二1.固然我们做的不是网址,不过也要有MVC的定义。发送给客户端的新闻应该相对独立。不能够从发送给客户端的新闻中触发属性的乘除。如果没发到呢,难道属性就绝不算了?

可是,难点在于内部已经有func(), japan= login(japan)
还没等用户调用,你就会内部推行那句的时候显把america执行了。应该是用户调用的时候再调用啊?

void GateApp::CGodWingManager::makeClientGodWingInfo(Message::Game::SPlayerGodWingInfo& playerGodWing, CPlayerPtr& player, CGateEntityPtr& gateEntity)
{
    。。。此处省略。。。

    if(_isNeedcalc)
    {
        getGodWingAllAttribute(gateEntity, player, playerGodWing.attributeInfo);
        getGodWingBaseAttribute(playerGodWing.baseAttribute);

        _allAttribute = playerGodWing.attributeInfo;
        _baseAttribute = playerGodWing.baseAttribute;

        _isNeedcalc = false;
    }
    else
    {
        playerGodWing.attributeInfo = _allAttribute;
        playerGodWing.baseAttribute = _baseAttribute;
    }
}

那边我们又可以用到嵌套函数,想实现一开端写 japan= login(japan)
不触发你函数的实践只须要在login里面再定义1层函数,第贰次调用 japan=
login(japan)值调用到外围的login,那几个login即使会履行不过不会接触认证,因为证实的全体的代码都被封装在login里层的新定义的函数里,
login只回去了里层的函数名,那样再实行japan()时,就会调用里层的函数。

2二.过度信赖json,写出来的代码,调节和测试的时候根本无法看到内部存款和储蓄器。

只怕大家也有点晕了,直接上代码:

void GateApp::CGodWingManager::getActivedSkinEquipSkills(int skinCode, Message::Public::DictIntInt& skillList)
{
    std::string temp1 = _activeSkinSkillJson[ToStr(skinCode)].asString();
    if(temp1.empty())
    {
        return;
    }

    Json::Value js;
    js.parse(temp1);

    std::string temp2 = js[ACTIVE_EQUIT_SKILL_LIST].asString();
    if(temp2.empty())
    {
        return;
    }

    std::vector<std::string> VecSkill;
    cdf::CStrFun::split(VecSkill, temp2.c_str(), ';');

    for(std::vector<std::string>::const_iterator it = VecSkill.begin(); VecSkill.end() != it; ++it)
    {
        std::vector<std::string> temp;
        cdf::CStrFun::split(temp, it->c_str(), ',');
        if(2 != temp.size())
        {
            continue;
        }

        skillList[atoi(temp[0].c_str())] = atoi(temp[1].c_str());
    }
}
def login(func):  # 把要执行的模块从这里传进来

    def inner():  # 再定义一层函数
        _username = 'dandy'  # 假装是DB抓取的用户信息
        _password = 'password.1'
        global user_status

        if user_status == False:
            username = input("user:")
            password = input("password:")

            if username == _username and password == _password:
                print("Welcome login...")
                user_status = True
            else:
                print("Wrong username or password !")

        if user_status == True:
            func()  # 只要验证通过就会调用相应的功能模块

    return inner  # 用户调用login的时候就会返回inner的内存地址,下次调用加上()才会执行inner函数

1层套1层的,非常小概看。如此复杂的构造,应该新建一个表来保存内容。

遵照python对于装饰器的定义,大家那儿能够把那一个代码去掉:

后台php总结的时候用到json也是很麻烦的,那样结尾照旧拖慢整个总结的快慢。

japan= login(japan) #你在这里相当于把japan这个函数替换了

只供给在供给装饰的函数方面加上下边包车型客车代码:

 

def home():
    print("---首页----")


def america():
    print("----欧美专区----")


@login
def japan():
    # login()#验证
    print("----日韩专区----")


@login
def dalu():
    # login()#验证
    print("----大陆专区----")

功能一样。太棒了。

 

您神采飞扬的望着那壹串代码,突然又想尝试能或无法传参给版块:

@login
def japan():
    # login()#验证
    print("----日韩专区----")
japan("3p")

结果发现报错了。难道这些装饰器不可能传参数么?

当然!如若不可能传参数,还介绍了干嘛

def login(func):  # 把要执行的模块从这里传进来

    def inner(arg1):  # 再定义一层函数
        _username = 'dandy'  # 假装是DB抓取的用户信息
        _password = 'password.1'
        global user_status

        if user_status == False:
            username = input("user:")
            password = input("password:")

            if username == _username and password == _password:
                print("Welcome login...")
                user_status = True
            else:
                print("Wrong username or password !")

        if user_status == True:
            func(arg1)  # 只要验证通过就会调用相应的功能模块

        return inner  # 用户调用login的时候就会返回inner的内存地址,下次调用加上()才会执行inner函数

咱俩给inner函数加了个arg壹参数func的地点也加了个arg一,那样调用会传播相应的版块。

再试2遍呢?

def login(func):  # 把要执行的模块从这里传进来

    def inner(arg1):  # 再定义一层函数
        _username = 'dandy'  # 假装是DB抓取的用户信息
        _password = 'password.1'
        global user_status

        if user_status == False:
            username = input("user:")
            password = input("password:")

            if username == _username and password == _password:
                print("Welcome login...")
                user_status = True
            else:
                print("Wrong username or password !")

        if user_status == True:
            func(arg1)  # 只要验证通过就会调用相应的功能模块

    return inner  # 用户调用login的时候就会返回inner的内存地址,下次调用加上()才会执行inner函数

就那样大家成功了装饰器与参数的调用。。。神奇呢。

上边贴上具有的代码:

起名 4起名 5

 1 #!/user/bin/env python
 2 # -*- coding: utf-8 -*-
 3 
 4 user_status = False
 5 
 6 
 7 def login(func):  # 把要执行的模块从这里传进来
 8 
 9     def inner(*args, **kwargs):  # 再定义一层函数
10         _username = 'dandy'  # 假装是DB抓取的用户信息
11         _password = 'password.1'
12         global user_status
13 
14         if user_status == False:
15             username = input("user:")
16             password = input("password:")
17 
18             if username == _username and password == _password:
19                 print("Welcome login...")
20                 user_status = True
21             else:
22                 print("Wrong username or password !")
23 
24         if user_status == True:
25             func(*args, **kwargs)  # 只要验证通过就会调用相应的功能模块
26 
27     return inner  # 用户调用login的时候就会返回inner的内存地址,下次调用加上()才会执行inner函数
28 
29 
30 def home():
31     print("---首页----")
32 
33 
34 def america():
35     print("----欧美专区----")
36 
37 
38 @login
39 def japan(style):
40     # login()#验证
41     print("----日韩专区----")
42 
43 
44 @login
45 def dalu():
46     # login()#验证
47     print("----大陆专区----")
48 
49 
50 japan('3p')

View Code

您很开心的带着代码去付出给高管。老板侃了也很乐意。

 

第1天,老总又提议了新的须要,要求允许用户挑选拔qq\weixin\weibo
认证。那时候对于你的话1切就没那么难了。

起名 6起名 7

#!/user/bin/env python
# -*- coding: utf-8 -*-

user_status = False


def login(auth_type):  # 把要执行的模块从这里传进来
    def auth(func):
        def inner(*args, **kwargs):  # 再定义一层函数
            if auth_type == 'qq':
                _username = 'dandy'  # 假装是DB抓取的用户信息
                _password = 'password.1'
                global user_status

                if user_status == False:
                    username = input("user:")
                    password = input("password:")

                    if username == _username and password == _password:
                        print("Welcome login...")
                        user_status = True
                    else:
                        print("Wrong username or password !")

                if user_status == True:
                    func(*args, **kwargs)  # 只要验证通过就会调用相应的功能模块
            else:
                print('wrong qq auth.')
        return inner  # 用户调用login的时候就会返回inner的内存地址,下次调用加上()才会执行inner函数
    return auth


def home():
    print("---首页----")


def america():
    print("----欧美专区----")


@login('qq')
def japan(style):
    # login()#验证
    print("----日韩专区----")


@login('weixin')
def dalu():
    # login()#验证
    print("----大陆专区----")


japan('3p')
dalu()

View Code

 

 

三、Json & Pickle
数据系列化

用于类别化的多个模块

· Json,用于字符串 和python数据类型间的更换

· Pickle,用于python特有项目和python的数据类型间的转移

Json模块提供了多少个功用:dumps、dump、loads、load

Pickle模块提供了四个功能:dumps、dump、loads、load

事实上Python对于Json所写的包裹功效已经很完美了,基本没什么好讲的了上代码吧:

#!/user/bin/env python
# -*- coding: utf-8 -*-
import json

dict_new = {'name': 'dandy',
            'age': 22,
            'salary': 30000
            }

with open("view2", 'w', encoding='utf-8') as fs:
    json.dump(dict_new, fs)#写入文件

咱俩看下文件

起名 8

然后

with open('view2', 'r', encoding='utf-8') as fs:
    dict_new = json.load(fs)
print(dict_new)

结果为:
{'name': 'dandy', 'age': 22, 'salary': 30000}

Pickle会加字符串转换到2进制:

import pickle

dict_new = {'name': 'dandy',
            'age': 22,
            'salary': 30000
            }
p_str = pickle.dumps(dict_new,)
print(p_str)
结果为一串二进制数:
b'\x80\x03}q\x00(X\x04\x00\x00\x00nameq\x01X\x05\x00\x00\x00dandyq\x02X\x03\x00\x00\x00ageq\x03K\x16X\x06\x00\x00\x00salaryq\x04M0uu.'

#写入文件二进制
with open('view3','bw')as fp:
    pickle.dump(dict_new, fp)

#读取文件
with open('view3','br')as fp:
    dict_new = pickle.load(fp)
print(dict_new)

结果:
{'name': 'dandy', 'age': 22, 'salary': 30000}

 

4、软件目录结构正式

为啥要规划好布局目录?

“设计项目目录结构”,就和”代码编码风格”一样,属于个人风格难题。对于那种风格上的正规,一向都存在三种态度:

  1. 1类同学以为,那种个人风格难点”无关重要”。理由是能让程序work就好,风格难题历来不是题材。
  2. 另1类同学认为,规范化能更好的主宰程序结构,让程序有所更高的可读性。

自家是相比较偏向于后人的,因为本人是前1类同学思想作为下的一向受害者。我曾经维护过三个特别不佳读的品类,其达成的逻辑并不复杂,不过却消耗了自己分外长的时间去领会它想发挥的意思。从此小编个人对于抓牢项目可读性、可维护性的供给就很高了。”项目目录结构”其实也是属于”可读性和可维护性”的规模,大家布署三个层次明显的目录结构,正是为着达到以下两点:

  1. 可读性高:
    不熟悉那些类型的代码的人,一眼就能看懂目录结构,知道程序运营脚本是哪些,测试目录在何地,配置文件在哪个地方之类。从而丰盛连忙的打听那几个类型。
  2. 可维护性高:
    定义好组织规则后,维护者就能很扎眼地通晓,新增的哪位文件和代码应该置身什么目录之下。这么些利益是,随着岁月的推移,代码/配置的局面扩展,项目组织不会混杂,照旧可以组织非凡。

于是,作者以为,保持二个层次分明的目录结构是有不可或缺的。更何况组织2个佳绩的工程目录,其实是1件很简单的事宜。

目录组织格局

至于如何协会一个较好的Python工程目录结构,已经有1对赢得了共同的认识的目录结构。在Stackoverflow的其一题目上,能观看大家对Python目录结构的议论。

那边面说的已经很好了,小编也不打算重新造轮子列举种种分化的法门,那之中小编说一下自身的明亮和体会。

设若你的门类名字为foo,
笔者比较建议的最方便急忙目录结构那样就足足了:

Foo/
|-- bin/
|   |-- foo
|
|-- foo/
|   |-- tests/
|   |   |-- __init__.py
|   |   |-- test_main.py
|   |
|   |-- __init__.py
|   |-- main.py
|
|-- docs/
|   |-- conf.py
|   |-- abc.rst
|
|-- setup.py
|-- requirements.txt
|-- README

简短解释一下:

  1. bin/:
    存放项目标一些可执行文件,当然你能够起名script/等等的也行。
  2. foo/: 存放项指标有所源代码。(一)
    源代码中的全数模块、包都应该放在此目录。不要置于顶层目录。(二)
    其子目录tests/寄存单元测试代码; (三)
    程序的进口最棒命名叫main.py
  3. docs/: 存放1些文档。
  4. setup.py:
    安装、安顿、打包的脚本。
  5. requirements.txt:
    存放软件正视的外部Python包列表。
  6. README: 项目表明文件。

除开,有壹些方案提交了进一步多的剧情。比如LICENSE.txt,ChangeLog.txt文本等,作者未曾列在此处,因为这一个事物首倘诺种类开源的时候需求用到。假若您想写3个开源软件,目录该怎样组织,可以参照那篇作品

上面,再简单讲一下本人对这几个目录的明亮和个体须要吗。

关于README的内容

其1自个儿以为是各种项目都应当有个别八个文本,指标是能大致描述该品种的音信,让读者一点也不慢了然这么些体系。

它要求证实以下多少个事项:

  1. 软件定位,软件的基本功效。
  2. 运维代码的点子:
    安装环境、运维命令等。
  3. 不难的使用验证。
  4. 代码目录结构表明,更详细点可以表达软件的基本原理。
  5. 大面积难题求证。

自己觉着有上述几点是比较好的三个README。在软件开发初期,由于开发过程中上述内容恐怕不备受关注可能发生变化,并不是毫无疑问要在1方始就将装有音信都补全。可是在品种甘休的时候,是须要写作那样的3个文书档案的。

能够参见Redis源码中Readme的写法,那里面简洁但是清晰的讲述了Redis作用和源码结构。

关于requirements.txt和setup.py

setup.py

相似的话,用setup.py来保管代码的包裹、安装、布置难题。产业界规范的写法是用Python流行的包装工具setuptools来治本这一个事情。这种措施广泛利用于开源项目中。可是那里的核心境想不是用标准化的工具来化解这一个难点,而是说,一个项目必将要有一个设置配置工具,能连忙便捷的在一台新机器元帅环境装好、代码安顿好和将程序运维起来。

这一个本人是踩过坑的。

自家刚起初接触Python写项指标时候,安装环境、安插代码、运维程序这几个进程全是手动完结,境遇过以下难题:

  1. 设置环境时平日忘了方今又添加了1个新的Python包,结果1到线上运营,程序就出错了。
  2. Python包的本子注重难点,有时候大家先后中运用的是二个版本的Python包,可是官方的早已是风尚的包了,通过手动安装就恐怕装错了。
  3. 万一借助的包很多以来,1个贰个安装这么些正视是很费劲的思想政治工作。
  4. 新校友开端写项指标时候,将顺序跑起来相当费力,因为恐怕时时忘了要怎么设置种种依赖。

setup.py能够将这个工作自动化起来,升高成效、收缩失误的可能率。”复杂的东西自动化,能自动化的东西一定要自动化。”是1个分外好的习惯。

setuptools的文档比较庞大,刚接触的话,或许不太好找到切入点。学习技能的办法正是看旁人是怎么用的,能够参照一下Python的1个Web框架,flask是哪些写的: setup.py

当然,简单点自身写个安装脚本(deploy.sh)替代setup.py也未尝不可。

requirements.txt

这几个文件存在的指标是:

  1. 有利于开发者维护软件的包正视。将支付进度中新增的包添加进那么些列表中,避免在setup.py设置注重时漏掉软件包。
  2. 方便读者显著项目利用了什么样Python包。

那个文件的格式是每1行李包裹涵三个包注重的证实,经常是flask>=0.10那种格式,须求是以此格式能被pip识假,那样就能够简单的通过 pip install -r requirements.txt来把富有Python包注重都装好了。具体格式表明: 点这里

 

有关配置文件的采取办法

瞩目,在下面的目录结构中,未有将conf.py位居源码目录下,而是位于docs/目录下。

有的是品类对安顿文件的选用做法是:

  1. 布署文件写在贰个或多个python文件中,比如那里的conf.py。
  2. 类型中哪些模块用到这些布局文件就径直通过import conf那种样式来在代码中运用安顿。

那种做法笔者不太协理:

  1. 这让单元测试变得紧Baba(因为模块内部依赖了外部配置)
  2. 一只配置文件作为用户控制造进度序的接口,应当能够由用户自由钦赐该文件的路子。
  3. 程序组件可复用性太差,因为这种贯穿全部模块的代码硬编码格局,使得超过三分之一模块都依靠conf.py其一文件。

故此,笔者认为配置的使用,更好的秘诀是,

  1. 模块的布局都以足以灵活配置的,不受外部配置文件的震慑。
  2. 程序的安顿也是足以灵活决定的。

能够佐证那个思想的是,用过nginx和mysql的同学都精通,nginx、mysql那一个程序都得以随心所欲的内定用户配置。

为此,不应该在代码中平昔import conf来选择安排文件。下边目录结构中的conf.py,是交由的三个布局样例,不是在写死在先后中央直机关接引用的安排文件。能够通过给main.py启航参数钦定布署路径的格局来让程序读取配置内容。当然,这里的conf.py你能够换个像样的名字,比如settings.py。大概您也可以利用此外格式的情节来编排配置文件,比如settings.yaml之类的。

 

沾满一份作业:

 

作业供给:

效仿实现一个ATM +
购物商城先后

  1. 额度
    1四千或自定义
  2. 贯彻购物商城,买东西出席购物车,调用信用卡接口结账
  3. 能够提现,手续费伍%
  4. 每月2二号出账单,每月10号为还款日,过期未还,按欠款总额
    极度之5 天天计算利息
  5. 支撑多账户登录
  6. 支撑账户间转账
  7. 笔录每月壹般消费流水
  8. 提供还款接口
  9. ATM记录操作日志 
  10. 提供管理接口,包括充裕账户、用户额度,冻结账户等。。。
  11. 用户认证用装饰器

 

起名,演示代码
https://github.com/triaquae/py3\_training/tree/master/atm 

简易流程图:https://www.processon.com/view/link/589eb841e4b0999184934329
 

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图